Files
fotospiel-app/docs/archive/prp/13-backend-authentication.md
2025-11-20 12:31:21 +01:00

130 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 13 - Backend Authentication Implementation
## Overview
Tenant authentication now uses a hybrid Sanctum setup:
- **Tenant Admin PWA** obtains Laravel Sanctum personal access tokens (PATs) via first-party endpoints under `/api/v1/tenant-auth/*`. Tokens carry ability strings (e.g. `tenant-admin`, `tenant:{id}`) and are required for all `/api/v1/tenant/*` routes.
- **First-party web flows** (marketing checkout, Filament admin, Google OAuth sign-in) rely on the classic session guard. When a session-based login needs API access (e.g. inside the PWA shell after a Google callback) the frontend exchanges the session for a PAT using the same Sanctum endpoints.
- **Legacy compatibility** is preserved through `/api/v1/tenant/me`, which now proxies the Sanctum-backed data while keeping the historical payload shape consumed by older clients.
All previous OAuth2/PKCE code paths, tables, and environment variables have been removed. No external authorization server is required.
## Tenant Admin PAT Flow
### Login
- **Endpoint**: `POST /api/v1/tenant-auth/login`
- **Body** (`application/json`):
```json
{
"login": "tenant@example.com",
"password": "secret"
}
```
`login` accepts either an email address or username. Passwords are validated against the user record.
- **Response** (`200`):
```json
{
"token": "plain-text-pat",
"token_type": "Bearer",
"abilities": ["tenant-admin", "tenant:42"],
"user": {
"id": 17,
"email": "tenant@example.com",
"name": "Tenant Admin",
"role": "tenant_admin",
"tenant_id": 42
}
}
```
- **Failure codes**:
- `422` with `login` field errors for invalid credentials, unverified email addresses, or roles without tenant access.
- `429` throttle when the `tenant-auth` rate limit is exceeded.
Every successful login revokes any previous `tenant-admin` PAT for that user before issuing a new token. PATs are plain-text once in the response; the hash is stored in `personal_access_tokens`.
### Token Exchange (session → PAT)
- **Endpoint**: `POST /api/v1/tenant-auth/exchange`
- **Guards**: `web` session (cookies) with Sanctum stateful middleware.
- **Response**: identical payload to `/login`.
- **Usage**: The Tenant Admin PWA calls this after a browser session login (e.g. Google Socialite callback or marketing checkout user who opens the PWA shell) to synchronise state without a password prompt.
### Token Introspection & Logout
- **`GET /api/v1/tenant-auth/me`** (auth:sanctum) returns the current user, tenant snapshot, and active abilities for the PAT.
- **`POST /api/v1/tenant-auth/logout`** invalidates the current PAT and clears it from storage.
- **Legacy Proxy**: `/api/v1/tenant/me` now delegates to the new controller to return the historical payload seen by existing tooling while relying on Sanctum for authentication.
### Calling Tenant APIs
All tenant APIs continue to require the abilities enforced by middleware:
- `auth:sanctum` authenticates the PAT.
- `tenant.admin` gate checks that the user has `tenant-admin` or `super-admin` ability.
- `tenant.isolation` ensures the `tenant:{id}` ability matches the route tenant, guarding cross-tenant access.
Requests must send the PAT in the `Authorization: Bearer {token}` header. Tokens have no server-side expiration (`sanctum.expiration = null`); clients should refresh proactively (e.g. re-login) when a token is revoked or rejected.
## Session-Based Flows
### Marketing Checkout Login
- **Endpoint**: `POST /checkout/login`
- **Body**: `{ identifier, password, locale? }`
- **Remember behaviour**: the checkbox is hidden on the new Tenant Admin login screen but applied automatically sessions persist via `Auth::login($user, true)`.
- Successful logins store a `redirect_intent` in the session when the flow originates from `/event-admin/*`, ensuring post-login navigation goes to `/event-admin/dashboard`.
### Tenant Admin PWA Login Screen
- The React shell renders `/event-admin/login` inside the PWA layout. It uses the same credentials form as `/checkout/login` but hits `/api/v1/tenant-auth/login` with fetch requests.
- After a successful PAT response, the token is persisted to both `localStorage` and `sessionStorage`; abilities are cached client-side to power feature flags.
### Google Sign-In
- Socialite handles the Google redirect/callback under `/event-admin/auth/google`.
- On success the user is logged into the web session, their email is marked verified (if needed), and the browser redirects back into the PWA shell.
- The shell immediately calls `/api/v1/tenant-auth/exchange` to obtain a PAT, keeping the flow consistent with password logins.
- Failures redirect back to the login page with query-string error codes that the PWA surfaces via i18n strings.
## Middleware & Guards
| Middleware | Purpose |
| --- | --- |
| `auth:sanctum` | Validates PATs issued by Sanctum. |
| `tenant.admin` | Ensures the authenticated user is a tenant admin or super admin. |
| `tenant.isolation` | Loads the tenant model by token ability and adds it to the request for downstream controllers. |
| `throttle:tenant-auth` | Rate limits login, exchange, and logout endpoints. |
| `EncryptCookies`, `AddQueuedCookiesToResponse`, `StartSession` | Applied to exchange routes so session cookies are available. |
## Security Considerations
- PATs are hashed at rest (`personal_access_tokens`), revocation is handled via database deletes.
- Tokens are limited to one active entry per user (`tenant-admin` name). Issuing a new token automatically stairs old devices out.
- All auth endpoints return generic validation errors to avoid username enumeration.
- Session regeneration occurs on every successful login (password or Google) to prevent fixation.
- Frontend storage keeps a second copy of the PAT in `sessionStorage` to reduce exposure when a user clears only persistent storage.
- Rate limits: configure `tenant-auth` in `RouteServiceProvider` (default `10 requests / minute`).
- CSP / XSRF: stateful Sanctum middleware is configured with `SANCTUM_STATEFUL_DOMAINS` so SPA requests inherit `XSRF-TOKEN` cookies automatically.
## Database & Infrastructure
- OAuth tables (`oauth_clients`, `oauth_codes`, `refresh_tokens`) have been removed. Sanctum uses the stock `personal_access_tokens` migration.
- No background jobs are required for PAT issuance or rotation.
- Audit requirements should log `personal_access_tokens` changes via database auditing (e.g. telescope/horizon logs) if needed.
## Configuration & Environment
Set the following environment variables to support the hybrid flow:
- `SANCTUM_STATEFUL_DOMAINS` comma-separated list of domains running the Tenant Admin PWA (TWA, Capacitor, staging). Ensures cookies are considered first-party for the exchange endpoint.
- `SANCTUM_TOKEN_PREFIX` optional prefix to enable secret-scanning detection.
- Remove the deprecated OAuth variables:
- `VITE_OAUTH_CLIENT_ID`
- `OAUTH_JWT_KID`
- `OAUTH_KEY_STORE`
- `OAUTH_REFRESH_*`
Google OAuth credentials (`GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`) remain for Socialite but only power the browser session login; token exchange happens through Sanctum.
## Testing
- Feature tests cover `/api/v1/tenant-auth/login`, `/exchange`, and `/logout` (`tests/Feature/Auth/TenantProfileApiTest.php`, `tests/Feature/TenantCreditsTest.php`).
- Frontend Playwright fixtures (`tests/e2e/utils/test-fixtures.ts`) provide helpers to obtain PATs when seeding test users.
- When adding new tenant API endpoints ensure coverage under Sanctum by using `->actingAs($user, 'sanctum')` in PHPUnit tests and by asserting abilities with `Sanctum::actingAs()` helpers where required.
This Sanctum-based approach keeps login logic inside the Laravel application, avoids custom OAuth infrastructure, and works uniformly across web, PWA, and the planned React Native wrapper.