# Authentication Hardening

## MFA and phone verification

MFA means multi-factor authentication: after password login, sensitive users prove a second factor such as a one-time SMS code, authenticator-app code, or security key.

Phone verification means proving the phone number belongs to the user, normally by sending a one-time code by SMS and verifying it before the number can be trusted for account recovery, ticket delivery fallback, or staff login alerts.

## MVP position

The local MVP uses secure PHP sessions, bcrypt password verification, CSRF tokens, role checks, login rate limiting, password reset tokens, email-verification-ready flows, and MFA challenge APIs.

The current hardening pass adds:

- Strict session cookie settings through `SessionCookiePolicy`.
- `HttpOnly` session cookies with `SameSite=Lax`.
- Secure cookies automatically when `APP_ENV=prod`, HTTPS is detected, or `APP_FORCE_HTTPS=true`.
- Session fixation protection with ID regeneration after login.
- Inactive/suspended user sessions are invalidated on the next authenticated request.
- CSRF enforcement for logout, checkout draft creation, seat holds, admin/organiser writes, account writes, media changes, and scanner device management.
- Hashed one-time MFA challenge storage in `mfa_challenges`.
- MFA request/status/verify endpoints at `/api/v1/auth/mfa/status`, `/api/v1/auth/mfa/request`, and `/api/v1/auth/mfa/verify`.
- MFA request and verify rate limiting with audit events for failed attempts.
- A local development adapter that returns `local_code` only outside production.
- Dedicated MFA email delivery config via `MFA_MAIL_PROVIDER`, `MFA_MAIL_HTTP_ENDPOINT`, and `MFA_MAIL_HTTP_BEARER_TOKEN` so login MFA does not reuse ticket delivery settings.
- Platform-admin phone verification policy controls in Admin Settings. The policy can be set to disabled, optional, required for privileged users, or required for all users, with a customer prompt and ticket-delivery fallback toggle.
- Customer phone verification endpoints at `/api/v1/account/phone-verification/status`, `/api/v1/account/phone-verification/request`, and `/api/v1/account/phone-verification/verify`.
- Phone verification uses hashed OTP codes in `mfa_challenges`, records queued SMS delivery logs, rate-limits requests and attempts, and marks `users.phone_verified_at` only after a valid code is confirmed.

For the first live MVP run, MFA enforcement is off by default so seeded users are not locked out. Set `MFA_ENFORCE_PRIVILEGED=true` only after `MFA_OTP_SECRET` and real email/SMS delivery are configured.

Phone verification is workflow-ready with a local `log` adapter and a generic signed HTTP SMS provider path. In local development the `log` adapter may return a `local_code` for manual testing. In production, the `log` adapter fails closed and raw OTP codes must never be returned to the browser; configure `SMS_OTP_PROVIDER=http`, `SMS_OTP_HTTP_ENDPOINT`, `SMS_OTP_HTTP_BEARER_TOKEN`, `SMS_OTP_SIGNING_SECRET`, rate limits, and delivery monitoring.

MFA email delivery is separate from ticket delivery. Configure `MFA_MAIL_PROVIDER=log` only for local development or `MFA_MAIL_PROVIDER=http` with `MFA_MAIL_HTTP_ENDPOINT` and `MFA_MAIL_HTTP_BEARER_TOKEN` for a production provider that accepts the verification email payload.

## Production requirements before public launch

- Enable admin and organiser MFA enforcement through `MFA_ENFORCE_PRIVILEGED=true`.
- Confirm the platform phone verification mode in Admin Settings before launch.
- Keep customer MFA optional.
- Use short-lived one-time codes for SMS verification.
- Store only hashed OTP codes, never raw codes.
- Rate limit OTP requests and verification attempts.
- Disable local-code responses in production by setting `APP_ENV=prod` or `APP_ENV=production` and configuring a real SMS provider.
- Use `SMS_OTP_PROVIDER=http` with an HTTPS endpoint that accepts the signed JSON payload. Failed provider delivery marks the OTP challenge failed.
- Log MFA enrolment, disablement, recovery, and failed attempts to audit logs.
- Use trusted SMS/email providers through adapters; never hard-code provider keys.
- Add backup recovery codes for platform admins.

## Environment placeholders

```env
MFA_ENFORCE_PRIVILEGED=false
MFA_OTP_SECRET=change-me-to-a-long-random-string
SMS_OTP_PROVIDER=log
SMS_OTP_SIGNING_SECRET=change-me-to-a-long-random-string
SMS_OTP_HTTP_ENDPOINT=
SMS_OTP_HTTP_BEARER_TOKEN=
MFA_MAIL_PROVIDER=log
MFA_MAIL_HTTP_ENDPOINT=
MFA_MAIL_HTTP_BEARER_TOKEN=
SESSION_NAME=zavvion_mvp
SESSION_SAME_SITE=Lax
```

The `log` provider is for local development only.
