Skip to main content

Authentication

Sessions (NextAuth)

  • Email + password (credentials) provider by default. Social / SSO sign-in isn't enabled out of the box but can be added via NextAuth OAuth providers (Google, Okta, Microsoft) — see the MFA note below.
  • bcrypt cost factor 12 for password hashes.
  • 10-failure lockout per email (User.failedLogins, User.lockedUntil); 30-minute lockout window.
  • 24-hour JWT expiry.
  • Cookies: __Secure-next-auth.session-token in production (HTTP-only, Secure, SameSite=Lax).
  • Cookie-session mutations are protected by SameSite=Lax cookies plus a same-origin Origin/Referer check (bearer-token API requests are exempt).

Passwords aren't stored, logged, or returned by any API. The admin UI's "Reset password" path goes through NextAuth.

API key issuance

Keys are generated as 32 bytes of cryptographic randomness (Node crypto.randomBytes), encoded as hex. Stored as SHA-256 hash; the original is shown exactly once at creation.

POST /api/v1/projects/{slug}/api-keys
{ "name": "Production frontend", "type": "delivery", "siteId": "site_main", "expiresAt": "2027-01-01T00:00:00Z" }

The response includes the plain key in data.key — the only time it appears. Audit log records the create event with the key prefix and the calling user.

Revocation flips isActive=false on the row. The key prefix lives forever in the audit log so you can attribute past actions to a specific (now-revoked) key.

Failed-login behavior

Per-email rate limit + lockout. Login attempts increment User.failedLogins; reaching 10 sets User.lockedUntil = now + 30min. The login endpoint returns 401 for both wrong-password and locked accounts (no info leak between the two).

Audit log captures every login attempt with the email + outcome + IP + user agent.

Session invalidation on password change

Planned — not yet implemented

There is currently no session invalidation on password change: existing JWTs remain valid for their full 24-hour expiry after a password is changed. The planned implementation adds a sessionVersion column on the user row that increments on password change, so the JWT carries the version it was issued under and is rejected when stale. This is not wired up today.

Multi-factor authentication

Not implemented in V3. Webauthn / passkeys are reserved for a future release. Customers needing MFA today should use SSO via NextAuth's OAuth providers (Google, Okta, Microsoft) — these are not enabled by default but can be configured.

API keys vs sessions

ActionSessionsAPI keys
Admin UI navigation
/api/v1/* mutations✅ (CSRF-gated)✅ (Bearer)
/api/delivery/* reads✅ (delivery / preview)
/api/graphql/*
/api/mcp/*❌ (sessions don't reach MCP)✅ (management / preview)

The auth resolver (authenticateRequest) chooses based on what's present — bearer header beats cookie when both are sent.