eetr-auth
Operations

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:

SecretDescription
AUTH_SECRETNextAuth.js session encryption secret (random 32+ byte string). Also derives the key that encrypts TOTP secrets at rest.
HMAC_KEYHMAC-SHA256 signing key for internal request validation.
JWT_PRIVATE_KEYRS256 private key (PEM) for signing access / ID tokens.
RESEND_API_KEYResend 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 kid is rendered into wrangler.generated.jsonc as JWT_KID by infra:render-wrangler / setup:remote — you don't set it by hand.
  • The jwks.json in R2 must contain the public half of the active signing key, and the CDN must serve that same kid (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-secrets

After rotating, always verify:

npm run verify:remote

This 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:remote

Rotating 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-auth

No workflow change is needed.

On this page