Skip to content

Keys, JWKS, and rotation

Select a key driver, publish public keys, and rotate without invalidating live tokens.

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

  1. Confirm every instance has the same APP_KEY/encryption key ring and can read the signing-key table.

  2. Confirm the configured access-token TTL and clock tolerance. This defines the minimum overlap.

  3. Run the command once from a release or scheduled job:

    node ace jwt:rotate-keys
  4. The driver introduces and promotes a new Ed25519 key, retaining the previous public key as verifying.

  5. Confirm newly issued tokens use the new kid and both keys appear in JWKS.

  6. Do not force retirement. A later rotation retires verifying keys only after more than one strict access TTL has elapsed since promotion.

  7. 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.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close