Skip to content

User Security Settings — Production Readiness

Audit date: 2026-09-02 · Branch: feature/user-security-settings

Verdict

Production-ready after remediation in this branch. Ship Backend, Frontend, and Docs together; run migrations before enabling the SPA login 2FA step.

Scope

CapabilityCentralTenantUI
Active browser sessionsProfile → Security
TOTP two-factor authenticationProfile → Security + login challenge
Passkeys (WebAuthn)Profile → Security + login button
Password change + revoke other sessionsProfile → Security

Audit summary

Resolved (this branch)

SeverityIssueFix
CriticalParallel Fortify web auth routes (POST /login, /two-factor-challenge, /user/passkeys, …)Fortify::ignoreRoutes() in AppServiceProvider::register()
CriticalLogin audit / markLoggedIn / attendance before 2FA completedDeferred until after 2FA gate or challenge completion
HighTenant 2FA challenge missing workspace bindingTwoFactorService::assertTenantChallenge() + user tenant_id check
Highconfig/passkeys.php unsafe env() at load + missing prod defaultsConfig-cache-safe file + boot-time RP ID / origins resolution
HighMissing PASSKEYS_* in .env.exampleDocumented with examples
Medium2FA disable allowed with password onlyRequires authentication or recovery code
MediumNo throttle on password-gated security mutationsthrottle:auth-sensitive (10/min per user) on /me/two-factor*, /me/passkeys*, change-password
MediumRecovery code regeneration not auditedtwo_factor_recovery_codes_regenerated audit event
FrontendWeak client password validationstrongPasswordSchema mirrors default PasswordRule
FrontendUnsanitized TOTP QR SVGsanitizeSvgHtml() before render
FrontendPasskey browser cancel UXFriendly message for NotAllowedError / AbortError
DocsMissing deployment / API / upgrade guidanceSee linked pages below

Accepted trade-offs

ItemNotes
Passkey login skips TOTPDocumented; passkey possession is the second factor
Full WebAuthn ceremony in PlaywrightOptions API + UI validation covered; browser ceremony flaky in CI
Branded custom domainsOperators must list each SPA origin in PASSKEYS_ALLOWED_ORIGINS
Mobile appDoes not yet implement 2FA challenge or passkeys

Pre-deploy checklist

1. Migrate

bash
php artisan migrate --force

Includes: passkeys table, two_factor_* on users / central_users, session metadata on personal_access_tokens.

2. Environment

VariableRequiredPurpose
FRONTEND_URLYesSPA origin (HTTPS); reset links + default passkey allowed origin
APP_URLYesPublic API origin (HTTPS)
PASSKEYS_RELYING_PARTY_IDRecommendedWebAuthn RP ID = registrable SPA domain (e.g. app.example.com)
PASSKEYS_ALLOWED_ORIGINSWhen multi-originComma-separated SPA origins (tenant subdomains, Branded custom domains)
PASSKEYS_USER_HANDLE_SECRETOptionalStable WebAuthn user handle secret (defaults to APP_KEY)
SESSION_SECURE_COOKIEYestrue in production

Fortify features remain enabled for Actions (TOTP/passkey internals); web routes are disabled. The product surface is exclusively /api/{central\|tenant}/v1/….

3. HTTPS

WebAuthn requires a secure context. Production SPAs must be served over HTTPS (localhost is exempt for local dev).

4. Smoke tests

Backend

bash
php artisan test --compact tests/Feature/Auth/

Frontend E2E

bash
npm run test:e2e:profile -- --project=chromium
npx playwright test e2e/tests/auth/auth.two-factor-challenge.spec.ts --project=auth

Manual (staging HTTPS)

  1. Profile → Security → enable TOTP → sign out → password login → TOTP challenge → dashboard
  2. Register passkey → sign out → passkey login
  3. Profile → Sessions → revoke another session
  4. Branded custom domain: confirm passkey register/login on that origin (after adding to PASSKEYS_ALLOWED_ORIGINS)

Test coverage

LayerCountStatus
Pest tests/Feature/Auth/20✅ Pass
Playwright profile security workflow1 serial✅ Pass
Playwright auth 2FA challenge2✅ Pass
Playwright profile specs3✅ Pass

Rollback

  • Frontend: revert SPA; users on old build cannot complete 2FA challenge UI (password-only login still works if 2FA not enabled per user).
  • Backend: do not roll back migrations that drop passkeys / 2FA columns on active users. Prefer forward fix.
  • Disable per-user 2FA via admin DB reset only in break-glass scenarios (two_factor_* columns null on affected rows).

Official documentation for the EloSync SaaS Platform.