V8 Central — Product & Technical Spec
  1. Docs
  2. Core platform
  3. Authentication

#Authentication

V8 Phase 2 (Authentication & Security) is "requirements complete" but not built V8 requirement Planned, not built. sa-central-api-2 has no auth endpoints yet Missing. Endpoint paths below are V7 references, not V8 contracts. V8 paths are decided when Phase 2 is planned.

#Feature list

#FeatureRules (V8 doc)V7 reference
2.1RegisterName, email, password (min 8). Cloudflare Turnstile. Optional referral / affiliate / invitation code + onboarding questionsPOST /users (also default_plan, company_size, industry, management_experience, heard_about_us, cf_token)
2.2Email verificationAccount inactive until verified. Resend allowed. Link valid 24 hGET /verify/{token}, POST /resend/verification-link
2.3LoginEmail + password. Separate messages for unverified and banned. Wrong credentials → one generic message. Success → token + user + organizations. Recorded in login history (IP, browser)POST /login → access_token, token_type, expires_in, user, organizations
2.4Google / GitHubRedirect → callback. New users registered. An existing email is linked automatically. Same 2FA + IP checksPOST /users/{provider}/url, GET /users/{provider}/callback (provider ∈ google, github)
2.5Forgot / resetReset email. Link valid 60 min. Reset logs out all devicesPOST /forgot-password, POST /reset-password
2.6Invitation passwordInvited user sets a password from the invite linkPOST /user/password-set/{token}
2.72FA at loginEmail code (10 min, max 5 wrong tries, resend), Google Authenticator, or backup code. Token only after 2FA passesPOST /two-factor-authentication/verify (email, code, google_auth), …/resend
2.8IP whitelistUnknown IP blocked, approval link emailed (24 h). Approved IP allowed afterwardsGET /user/whitelist-ip/{key}/authorize
2.9TokensBearer 15 days + refresh 30 days. Refresh gives a new token and revokes the oldPassport in V7 too
2.10LogoutRevokes the current tokenGET /user/logout
2.11SecurityRate limits on register, login, 2FA, resends, forgot password, IP approvalthrottle:ratelimit

#Login flow

StateFrontend behaviour
IdleEmail + password fields. Submit disabled until both are valid (email format, password 8–64 chars as in V7)
LoadingButton spinner, fields read-only, double submit blocked
Validation error (422)Field messages from errors
Wrong credentialsOne generic message ("Incorrect email or password"). Never say which one is wrong
UnverifiedSpecific message + "Resend verification email"
Banned / lockedSpecific message, no retry
IP not whitelisted"We emailed an approval link for this IP" screen
2FA requiredCode screen. Method switch (email code / authenticator / backup code), resend with cooldown, attempts left
Rate limited (429)"Too many attempts, try again in N s" from Retry-After
Network / 5xxInline error + Retry. No redirect
SuccessStore the session (see below), set current organization, go to ?next= (same-origin path only) or /[locale]/dashboard
V7 reference: login response shapes (not a V8 contract)
// 2FA off — POST /login
{"access_token": "eyJ…", "token_type": "Bearer", "expires_in": 1767225600,
 "user": {…}, "organizations": [{…}]}

// 2FA on — token withheld until POST /two-factor-authentication/verify
{"access_token": null, "token_type": null, "expires_in": null,
 "user": {…}, "organizations": null}

#Registration flow

  • An organization is not created at registration. V8 requirement
  • An initial plan at registration: V7 accepts default_plan (Pro / Standard) and creates Free/trial plans. V8 hasn't decided Open question.
  • An invitation code in the URL pre-fills and links the pending membership. V7 only invitation_token
  • Dropped from V7: throwaway-email block, Gmail-dot check, device fingerprint. V8 requirement

#OAuth (Google / GitHub)

  • Keys are managed in the admin panel, and read from the database until it exists. V8 requirement
  • Failure cases: provider disabled ("configuration is disabled" in V7), consent cancelled, email missing from provider, state mismatch. Each returns to /[locale]/login?error=<code> with a translated message. Assumption error codes still to define.

#Logout

  1. FE calls logout, which revokes the token on BE. V8 requirement 2.10
  2. FE clears the session cookie / token store, Zustand stores (current organization, cached lists) and any per-user cache.
  3. Redirect to /[locale]/login. Logout links are not prefetched.

#Current user / session restore

  • On every protected page load, the app needs current user + organizations to render the shell. V7 has GET /me and GET /auth/user/organizations. V8 needs the same Missing.
  • If the token is expired, use the refresh token once (V8 2.9). If that fails, clear the session and go to login with ?next=.

#Session handling (frontend)

Required by project rules: "cookie-based session for SSR", tokens never in client bundles, cookies httpOnly, secure, sameSite=Lax. Required by V8 doc: Passport bearer + refresh tokens.

Recommended (needs approval): a BFF pattern inside Next.js.

  • Next.js route handlers (/api/auth/*) call sa-central-api-2 login/refresh/logout and keep the access and refresh tokens in httpOnly cookies.
  • Server components and route handlers attach Authorization: Bearer when calling the API. The browser never reads the token.

Alternatives (Sanctum SPA cookies, or a token in memory) conflict with the Passport decision or the cookie rule. Open question Decision D-1 in Open questions.

#Protected routes

  • Public: login, register, verify, forgot/reset, invitation accept, IP-approval result, OAuth callback.
  • Logged-in users who open /login or /register are sent to the dashboard.

#Account features (Phase 3, reached from the user menu)

Profile, change email (confirm link to new address + notice to old), change password (logs out other devices), email preferences, delete protection, delete account (password; negative balance must be paid first), 2FA + backup codes (stored encrypted), Google Authenticator (on only after one correct code), IP whitelist (turning it on adds the current IP), login history, account activity, API access on/off, active sessions with log out one/all (new in V8). V8 requirement Phase 3

#Not supported / not decided

ItemState
Username loginNot supported Confirmed
Magic link / passkeysNot in any source
"Remember me"Not specified. Token lifetimes are fixed (15/30 days) V8 requirement
Session expiry UXRefresh once, then re-login. Exact UX Assumption
Confirmation timer (V7 confirmation_timer)"Ignored for now" V8 requirement Phase 3
ServerAvatar V8 Central · prepared by central-app-2 (Pair 2 frontend) for Bhavik Jethwa · nothing in this spec is implemented yet · Built 2026-10-03 12:35 UTC