Guide
Copy into .env.local or your host secret manager. Required keys boot the admin login; everything else depends on which features you enable.
ACCESS_USERNAMErequiredAdmin username on the login form
ACCESS_USERNAME=adminACCESS_PASSWORDrequiredStrong password for that account
ACCESS_PASSWORD=your-secure-passwordJWT_SECRETrequired≥32 chars; signs session JWTs
JWT_SECRET=your-super-secret-jwt-key-minimum-32-charactersnode -e "console.log(require('crypto').randomBytes(32).toString('hex'))"ACCESS_WEBAUTHN_SECREToptionalJSON from /webauthn (credentialID, Base64 publicKey, rpId, username).
ACCESS_WEBAUTHN_SECRET={"credentialID":"...","publicKey":"...","rpId":"...","username":"..."}JWT_EXPIRES_INoptionalUsed when Remember me is checked (e.g. 5m, 12h, 30d; defaults to 30d if unset). If Remember me is unchecked on the login form, the session JWT always expires in one day regardless of this value.
ALLOWED_REDIRECT_URLSoptionalComma-separated callback bases (scheme + host + optional port). Wildcards such as https://*.example.com are supported. The same list is also used to decide which Origin headers may call /api/auth/verify from the browser (HTTPS required in production).
ALLOWED_REDIRECT_URLS=https://app1.test,https://*.dev.localIf omitted, only same-origin relative redirectUrl values are allowed, and verify calls without a matching allowlist may be rejected for cross-site origins.
ECDH_SERVER_PRIVATE_KEYwhen using ECDHServer PEM for deriving shared secrets.
ECDH_SERVER_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----"…"-----END PRIVATE KEY-----"ECDH_SERVER_PUBLIC_KEYdebugLocal PEM convenience only. Production clients should call /api/oauth/public-key.
These keys are optional. Enable them when you need stricter token hygiene, stable OIDC-style iss / sub, or automated ECDH key rollover backed by Upstash Redis (REST).
ENABLE_TOKEN_REPLAY_PROTECTIONoptionalSet to 1 or true to record used JWT jti values in Upstash Redis so the same callback token cannot be posted twice. Requires AUTH_KV_REST_API_URL / AUTH_KV_REST_API_TOKEN (or UPSTASH_REDIS_REST_* / legacy KV_REST_API_* pairs). After verification, the original token is marked used; the new access_token from /api/auth/verify is for your downstream APIs—plan expiry and rotation there separately.
OAUTH_ISSUERoptionalOverrides the iss claim when you want a stable issuer instead of deriving it from the deployment URL.
USER_SUB_SALToptionalSalt for deriving the configured user's sub. If unset, the first 32 characters of JWT_SECRET are used—set an explicit salt when rotating JWT signing keys without changing subject identifiers.
ENABLE_KEY_ROTATIONoptionalSet to 1 or true to rotate server ECDH keys using the same Redis credentials as replay protection.
KEY_ROTATION_TTL_SECONDSoptionalLifetime of a key pair in seconds (default seven days).
KEY_ROTATION_TRANSITION_SECONDSoptionalOverlap window where old and new public keys may both be advertised (default one day).
JWT_SECRET and all derived sessions when you suspect compromiseACCESS_TOTP_SECRET or WebAuthn JSON, confirm login once on a staging URL before promoting