effect-auth

Production Checklist

Block unsafe effect-auth releases before production traffic.

Alpha software

effect-auth is dogfood alpha, not production-certified. Pin exact versions, inspect release and migration changes, and validate the complete deployment on your own risk model. Stop the release on any FAIL below.

A safe authentication release proves data recovery before promotion

Secrets and cryptography

  • Generate high-entropy secrets outside source control and isolate environments.
  • Derive scoped keys with AuthSecretsFromRootLive, or independently rotate every enabled secret domain.
  • Verify every shared root/session/challenge/privacy key is 32-1024 UTF-8 or raw bytes and startup fails on SecurityConfigurationError.
  • Use the built-in login-risk HMAC factory with a scoped 32-1024-byte key, or prove an expert custom factory provides equivalent domain separation; cache keys and device keys must be canonical digests and never raw IP/device material. Keep locationKey bounded coarse context only.
  • Keep generated challenge/session IDs and session bearer secrets within the documented 16-128-byte bounds; do not weaken test configuration in production.
  • Use WebCryptoLive() on Workers; never assume a Node crypto implementation exists.
  • Select and benchmark password hashing for the runtime. Workers use a reviewed PBKDF2 policy; supported Node/Bun deployments should prefer the Argon2id adapters.
  • For managed TOTP, protect TotpSecretCipher keys, retain old keys during rotation, and remove plaintext migration support only after all rows are encrypted.
  • For retained OAuth provider tokens, use the strict OAuthProviderTokenCipher layer with independent exact 32-byte keys, one current key, retained decrypt keys, complete-snapshot refresh CAS evidence, and concurrent refresh/revoke tests. Time-box and remove the explicit alpha legacy migration callback.
  • Document rotation, emergency revocation, and session impact.

FAIL: a default, missing, shared, logged, or plaintext configuration secret can reach production.

Browser boundary

VerifyRelease condition
TopologyPrefer same-origin /auth/*; test exact credentialed CORS if cross-origin
CookiesHttpOnly, Secure, intended SameSite, path/domain, and valid __Host- constraints on real HTTPS
Origin policyExact production allowlist; hostile and missing origins rejected for every method except exact case-sensitive GET/HEAD/OPTIONS tokens
Proxy metadataForwarded IP/protocol trusted only behind a controlled proxy or service binding
WebAuthnProduction RP ID and origin tested in a real browser

Choose and record one CSRF model: maintained origin-only, or origin plus app-owned double-submit. The exact fail-closed origin policy is mandatory in both models. If adding double-submit, verify token entropy, separate browser delivery, rotation/expiry, and authenticated-session binding. Origin checks do not configure CORS. FAIL: a hostile origin mutates state, cookies disappear, clients choose their policy IP, or the CSRF choice has no owned lifecycle.

Sensitive cookieEffective SameSite policyOther invariant attributes
SessionOptional explicit lax, strict, or none; default laxSecure, HttpOnly, __Host-, Path=/, no Domain
Trusted deviceOptional explicit lax, strict, or none; default laxSecure, HttpOnly, __Host-, Path=/, no Domain
Login approvalOptional explicit lax, strict, or none; default laxSecure, HttpOnly, __Host-, Path=/, no Domain
Email processFixed laxSecure, HttpOnly, __Host-, Path=/, no Domain
OAuth flowValidated omitted/query gives Lax; exact form_post gives NoneSecure, HttpOnly, __Host-, Path=/, no Domain

AuthDomainConfigLive({ sessionCookie }) intentionally affects only the session cookie. It does not ambiently discover or configure trusted-device, login-approval, email-process, or OAuth-flow cookie policy.

Durable infrastructure

  • Keep auth records in D1 or another durable database; keep shared limiter state in a Durable Object or equivalent, never isolate memory.
  • Select the correct migration path: fresh generated baseline or upgrades for the existing ledger, never both.
  • Apply reviewed migrations before dependent code and verify the production ledger afterward.
  • Schedule encrypted, access-controlled backups; rehearse restore into an isolated environment.
  • Exercise sign-in, registration, verification, reset, MFA, passkey, and recovery limits by every configured key.
  • Test 429, shared cross-instance coordination, email delivery responsibility, provider failure, expiry, and replay.

FAIL: restore is unproven, a required store is absent, limits reset on isolate restart, or email reports success before durable delivery responsibility exists.

Sessions and optional features

FeatureRequired evidence
SessionsValid TTL ordering; issue, refresh, rotate, logout, expire, revoke, and revoke-other-sessions against durable rows
Trusted deviceInteger TTL from 1 second through 400 days; commit immediately after durable trust while at least one whole second remains; malformed rows fail closed
Step-upRecent required AAL for factor, recovery, billing, and other sensitive actions
AuthorizationEvery backend operation checks app policy independently of route and UI guards
OAuthExact redirect URIs, state/PKCE, validated query/form_post callback mode, linking policy, strict AAD-bound provider-token envelopes, retained-key rotation evidence; no wildcards
Device CLICanonical HTTPS endpoints, bounded exact scopes, explicit observer failure policy, timeout/response/attempt ceilings, OS-keychain credential custody
PasskeysOne-time challenges, counters, enrollment, revocation, production RP ID/origin
RecoveryCodes are one-time, hashed, rate-limited, and regenerate by replacing the prior set
Login riskGeoIP TTL 1s-24h, precision 0-6, exact failure mode, cache capacity 1-10,000, malformed rows/provider output fail closed or follow explicit ignore policy
ProvidersExact HIBP/Turnstile/IPQS options, explicit cache/provider/observation failure modes, bounded responses and in-flight work, finite telemetry outcomes

Disable unlaunched routes, bindings, credentials, and UI. FAIL: preview and production credentials, callbacks, RP IDs, or recovery material are interchangeable.

Configuration inventory remains incomplete, so there is intentionally no checked broad “all configuration hardened” gate. Login-risk enrichment construction, HMAC inputs, packaged provider/cache rows, process-local capacity, provider-token cipher/key custody, AuditLog, SecurityTimeline, packaged-provider telemetry, and OAuth device-client boundaries are bounded and maintained. Device CLI releases must keep observer/log/span data value-free and test an abort-ignoring transport, late unused responses, hanging/rejecting body cancellation, malformed/fatal-UTF-8 and RFC token/error fields, request-timeout retry, actual local expiry, slow_down, and attempt capacity against the selected runtime. Other residual areas are app-owned full public-suffix validation for passkey RP IDs, deployed browser E2E coverage, trusted proxy selection, and durable limiter capacity. Maintained OAuth/device HTTP operations have standard throttling, but the broader architecture gate remains open for those deployment concerns.

Before deploying the AuditLog strict cutover, verify D1 backup or Time Travel coverage and PostgreSQL transactional migration execution. Expect every legacy audit row to move to quarantine with requires-runtime-normalization; replay only reviewed rows through normalizeAuditEvent and AuditLogStore.insertMany.

Before deploying the SecurityTimeline strict cutover, apply SQLite/D1 0036 or PostgreSQL 0017 in staging and inspect quarantine. Expect all legacy timeline rows to leave the active read model; replay only reviewed rows through normalizeSecurityTimelineEvent and the timeline store insert path.

Observability and privacy

  • Emit stable reason codes for auth failures, limiter decisions, provider errors, migration failures, and storage latency.
  • Alert on baseline deviation and distinguish attack, dependency failure, and release regression.
  • Redact cookies, credentials, codes, hashes, secrets, raw email/IP values, OAuth tokens, hostile headers, and complete auth rows from logs and traces.
  • Define access and retention; keep health checks read-only.

FAIL: operators cannot diagnose a failed flow without exposing authentication material.

Cloudflare and Alchemy v2

  • Pin the tested Alchemy, effect-auth, Effect, adapter, and Worker compatibility versions.
  • Bind AUTH_SECRET with Config.redacted, not a literal Worker environment value.
  • Use separate D1 databases, secrets, email bindings, Durable Objects, and Alchemy state per environment.
  • Apply D1 migrations during deployment, not in request handling.
  • Run local Alchemy/workerd tests and a deployed non-production D1 smoke against the public frontend URL.
  • Protect alchemy destroy, Cloudflare credentials, state files, deployment output, and logs.

The repository's Cloudflare checks use fake D1 for adapter tests and local Alchemy/workerd for the split-app smoke. They do not prove deployed D1 behavior. Pin a Worker compatibility date accepted by your Alchemy version; with nodejs_compat, keep it at or after Cloudflare's documented 2024-09-23 threshold and review compatibility-date changes before rollout.

Release gate

  • Unit, contract, concurrency, HTTP, browser, hostile-origin, replay, expiry, and revocation tests pass.
  • Canary owner, abort threshold, prior artifact/configuration, and rollback procedure are recorded.
  • Old and new code can overlap with the deployed schema; prefer additive changes.
  • Production-domain smoke covers sign-in, session read, refresh/logout, email, enabled continuation flows, and multiple Set-Cookie headers.

FAIL: telemetry is blind, smoke fails, migrations outrun compatible code, or rollback requires deleting production auth data.

Review Storage, Browser Client, Cloudflare Workers, and Alchemy v2 before approval.

On this page