Files
goodgo-platform/docs/security/secret-rotation.md
Ho Ngoc Hai 6afe4fd626 feat(auth): implement dual-key JWT verification for zero-downtime rotation
Add JWT_SECRET_NEXT env var support for seamless JWT secret rotation:

- JwtStrategy: use secretOrKeyProvider to try primary then fallback key
- TokenService.verifyAccessToken(): dual-key fallback for internal callers
- Redis metric jwt_verify_with_next_total for monitoring cut-over progress
- Session revocation marker support restored in JwtStrategy.validate()
- Unit tests for all three verification scenarios (primary, fallback, both-fail)
- docs/security/secret-rotation.md runbook with step-by-step rotation procedure

Closes GOO-203.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-24 12:08:34 +07:00

34 lines
1.1 KiB
Markdown

# JWT Secret Rotation Runbook
Zero-downtime JWT secret rotation using dual-key verification.
## Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| `JWT_SECRET` | Yes | Primary signing and verification key |
| `JWT_SECRET_NEXT` | No | Fallback verification-only key during rotation |
## How It Works
- **Signing**: Always uses `JWT_SECRET` (primary). Tokens are never signed with `_NEXT`.
- **Verification**: Tries `JWT_SECRET` first. On failure, falls back to `JWT_SECRET_NEXT` if set.
- **Metric**: Each fallback verification increments `metrics:jwt_verify_with_next_total` in Redis.
## Rotation Procedure
1. Generate new secret: `openssl rand -base64 48`
2. Deploy with `JWT_SECRET=<old>`, `JWT_SECRET_NEXT=<new>`
3. Swap: `JWT_SECRET=<new>`, `JWT_SECRET_NEXT=<old>`
4. Wait 15 minutes (access token TTL)
5. Drop `JWT_SECRET_NEXT`
6. Verify no 401 spikes
## Rollback
Swap back: `JWT_SECRET=<old>`, `JWT_SECRET_NEXT=<new>`.
## Emergency: Forced Re-login
Rotate to a new secret without setting `_NEXT`. All existing tokens become invalid.