JWT Key Management
Allegro signs member session tokens with per-tenant RSA-2048 key pairs using RS256. This page covers the key lifecycle and how to manage keys in production.
Key Lifecycle
active → retiring → expired
| Status | Description |
|---|---|
active | The current signing key. All new tokens are signed with this key. Appears in JWKS. |
retiring | A previously active key after rotation. Still appears in JWKS until retires_at. Allegro accepts tokens signed by this key indefinitely (until the token's own exp). |
expired | No longer appears in JWKS. Allegro still accepts tokens signed by this key until the token's own exp (1-year TTL). |
Retiring keys stay in JWKS for 1 year (matching the JWT TTL) so that external consumers like AWS API Gateway can always verify any valid token.
Commands
Generate Initial Keys
Run once per tenant after provisioning (automatically called by tenant:create):
php artisan jwt:keys:generate --tenant=*
Idempotent — skips tenants that already have an active key. Omit --tenant=* to run for the current tenant context.
Rotate Keys
Generates a new active key and moves the current active key to retiring:
php artisan jwt:keys:rotate --tenant=*
All new tokens are signed with the new key immediately. Old tokens remain verifiable via JWKS for 1 year.
Prune Expired Keys
Marks retiring keys as expired once retires_at has passed. Runs daily via the scheduler — you should not need to run this manually:
php artisan jwt:keys:prune --tenant=*
Expired keys are not deleted — Allegro continues to accept tokens signed by them until the token's own exp. Only JWKS visibility is removed.
Scheduler
jwt:keys:prune is registered in the Laravel scheduler and runs daily automatically. No additional configuration is required.
Emergency Key Revocation
There is no automated revocation. If a private key is compromised:
- Run
php artisan jwt:keys:rotate --tenant=<id>to immediately stop signing new tokens with the compromised key. Existing tokens signed by the compromised key remain verifiable via JWKS until you complete step 3. - The compromised key enters
retiringstatus and continues to appear in JWKS for up to 1 year (itsretires_at). External consumers will still accept tokens signed by it during this window. - If immediate revocation is needed, manually update the compromised key's
statustoexpiredin the tenant database. This removes it from JWKS immediately. However, external consumers (e.g., AWS API Gateway) cache JWKS responses — typically for up to 1 hour — so tokens signed by the compromised key may continue to be accepted until those caches expire. Clear JWKS caches on your external consumers where possible.