Environment keys
Use node ace jwt:make-key for a single issuer whose secret manager deploys PEM values to every
instance. Replace literal newlines with \n only when your environment format requires it; the
generated config restores them before importing the keys.
Changing an environment private key immediately invalidates tokens signed by the previous key. For zero-downtime rotation, use the database driver.
Encrypted database keys
keys: keyDrivers.database({ table: 'jwt_signing_keys' })Private JWK material is encrypted through AdonisJS Encryption; public material and key IDs remain
available for verification and JWKS. All application instances read the same active and overlapping
keys.
Rotation runbook
-
Confirm every instance has the same
APP_KEY/encryption key ring and can read the signing-key table. -
Confirm the configured access-token TTL and clock tolerance. This defines the minimum overlap.
-
Run the command once from a release or scheduled job:
node ace jwt:rotate-keys -
The driver introduces and promotes a new Ed25519 key, retaining the previous public key as
verifying. -
Confirm newly issued tokens use the new
kidand both keys appear in JWKS. -
Do not force retirement. A later rotation retires verifying keys only after more than one strict access TTL has elapsed since promotion.
-
Alert on command failure and
jwt:key_rotated; never run concurrent rotation jobs.
Refresh tokens are opaque and unaffected by signing-key rotation.
Public JWKS
When enabled, GET /.well-known/jwks.json returns verification keys with
Cache-Control: public, max-age=<cacheMaxAge>. Private JWK parameters (d, p, q, dp, dq,
qi, oth, and symmetric k) are never returned.
Choose cacheMaxAge shorter than the planned introduce-to-promote window so downstream verifiers
can fetch the new public key before it signs traffic.
Verify an external issuer
keys: keyDrivers.remoteJwks({
url: 'https://id.example.com/.well-known/jwks.json',
cacheMaxAge: 600_000,
cooldownDuration: 30_000,
timeoutDuration: 5_000,
})Remote JWKS is verification-only and honors alg, kid, use, and key operations during key
selection. Pin issuer, audience, and the asymmetric algorithm to the external provider’s exact
contract. Use HTTPS and do not accept a user-controlled JWKS URL.
HS256
HS256 is available for controlled symmetric deployments:
keys: keyDrivers.env({ secret: env.get('JWT_SECRET'), kid: 'hmac-2026-08' })The secret must contain at least 32 bytes. Every verifier that receives it can also mint tokens, so prefer Ed25519 when verification crosses service boundaries. HS256 secrets are never exposed via JWKS.