Secrets & key rotation
The secrets eetr-auth needs, how JWT signing keys and the published JWKS stay consistent, and how to rotate them safely.
The secrets
Set via wrangler secret put or provisioned by npm run infra:provision / setup:remote:
| Secret | Description |
|---|---|
AUTH_SECRET | NextAuth.js session encryption secret (random 32+ byte string). Also derives the key that encrypts TOTP secrets at rest. |
HMAC_KEY | HMAC-SHA256 signing key for internal request validation. |
JWT_PRIVATE_KEY | RS256 private key (PEM) for signing access / ID tokens. |
RESEND_API_KEY | Resend API key for transactional email. |
Provisioning preserves secrets by default
npm run infra:provision (and setup:remote / upgrade:remote) provision only missing secrets
by default — existing values are preserved. You rotate explicitly, never as a side effect.
The signing key ↔ JWKS relationship
Tokens are signed with JWT_PRIVATE_KEY and verified by relying parties against the public JWKS
served from R2/CDN at JWKS_CDN_BASE_URL. These must stay consistent:
- The active key's
kidis rendered intowrangler.generated.jsoncasJWT_KIDbyinfra:render-wrangler/setup:remote— you don't set it by hand. - The
jwks.jsonin R2 must contain the public half of the active signing key, and the CDN must serve that samekid(no stale cache).
If the signing key and published JWKS drift, issued tokens fail verification at /userinfo and
/token/validate.
Rotating JWT signing material
Rotate only when you intend to. The force-rotate flag regenerates AUTH_SECRET, HMAC_KEY, and JWT
material:
npm run setup:remote -- --force-rotate-secrets # clean install
# or, on an existing deployment:
npm run upgrade:remote -- --force-rotate-secretsAfter rotating, always verify:
npm run verify:remoteThis confirms the deployed discovery document, that the R2 source JWKS and the CDN JWKS serve the same
kid, and that the secrets are set. To also verify a freshly minted token's signature against the
published JWKS:
VERIFY_CLIENT_ID=... VERIFY_CLIENT_SECRET=... npm run verify:remoteRotating AUTH_SECRET affects TOTP secrets
TOTP secrets are encrypted at rest with a key derived from AUTH_SECRET. Rotating AUTH_SECRET
changes that derived key, which affects the ability to decrypt existing enrolled authenticators.
Treat AUTH_SECRET rotation as a deliberate operation and communicate it to users if it forces
re-enrollment.
The GIST_SECRET (maintainers)
Separate from the deployment: the repo's coverage badge is written to a public gist by a workflow
using the GIST_SECRET repo secret (a classic PAT with the gist scope). To rotate it, mint a new
gist-scoped PAT and run:
gh secret set GIST_SECRET --repo eetr-ai/eetr-authNo workflow change is needed.