- Sanctum personal access tokens via `/api/v1/tenant-auth/*`, persisted to IndexedDB/Keychain and refreshable through the session→token exchange for Google sign-ins.
- Tokens carry `tenant_id` and roles; backend enforces scoping.
Track JWT key rotation via `oauth:rotate-keys`, roll out dual-key support (old/new KID overlap), surface refresh-token revocation tooling, and extend IP/device binding rules for long-lived sessions. See `docs/deployment/oauth-key-rotation.md` for the rotation playbook. Filament now offers a refresh-token console with per-device revocation and audit history.
Track Sanctum PAT issuance and revocation. Provide tooling to list/revoke active PATs per tenant admin and document forced re-login procedures for compromised devices.
- **Guest Join Tokens** — *Owner: Guest Platform*
Hash stored join tokens, add anomaly metrics (usage spikes, stale tokens), and tighten gallery/photo rate limits with visibility in storage dashboards. Join-token access is now logged to `event_join_token_events` with summaries surfaced in the Event admin modal.
This document outlines the authentication requirements and implementation details for the Fotospiel tenant backend. The system uses OAuth2 with PKCE (Proof Key for Code Exchange) for secure authorization, providing tenant-specific access tokens for API operations. Additionally, session-based authentication is used for web interfaces like the checkout wizard, supporting both email and username login.
Tenant authentication now uses a hybrid Sanctum setup:
## Session-Based Authentication (Web/Checkout)
- **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.
### Checkout Login Flow
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`.
- **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`
- **Method**: POST
- **Content-Type**: `application/json`
- **Parameters**:
- `identifier`: Email or username (required, string)
- `password`: User's password (required, string)
- `remember`: Remember me flag (optional, boolean)
- `locale`: Language locale (optional, string, e.g., 'de')
- **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`.
**Authentication Logic**:
- Validate input using Laravel Validator.
- Search for user by email or username using Eloquent query:`User::where('email', $identifier)->orWhere('username', $identifier)->first()`.
- Verify password with `Hash::check()`.
- If valid, log in user with `Auth::login($user, $remember)` and regenerate session.
- Set `pending_purchase = true` if a package is selected (from session) and not already set, wrapped in DB transaction.
- Return JSON response with user data for AJAX handling in frontend.
### 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.
**Response** (JSON, 200 OK):
```json
{
"user": {
"id": 1,
"email": "user@example.com",
"name": "John Doe",
"pending_purchase": true
},
"message": "Login erfolgreich"
}
```
### 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.
- `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_*`
**Response**: Same as token exchange (new access and refresh tokens).
Google OAuth credentials (`GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`) remain for Socialite but only power the browser session login; token exchange happens through Sanctum.
### 4. Token Validation
- **Endpoint**: `GET /api/v1/tenant/me`
- **Method**: GET
- **Authorization**: `Bearer {access_token}`
- **Purpose**: Validate token and return tenant information
## Testing
**Response** (JSON):
```json
{
"tenant_id": "tenant-uuid",
"tenant_name": "Event Photo Company",
"credits": 150,
"role": "admin",
"email": "admin@eventphoto.com"
}
```
- 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.
## Security Requirements
### PKCE Implementation
- Validate `code_challenge_method` is `S256`
- Store `code_challenge` and `code_verifier` association temporarily (Redis, 5-minute expiry)
- Verify SHA-256 hash of `code_verifier` matches stored `code_challenge` during token exchange
- Reject requests without valid PKCE parameters
### State Validation
- Generate cryptographically secure state parameter
- Store state in session or Redis with 5-minute expiry
- Validate state parameter matches stored value during callback
- Reject requests with state mismatch (CSRF protection)
### Token Security
- Access tokens: JWT with 1-hour expiry, signed with RS256
- Refresh tokens: Secure random 128-character strings, stored in database
- Refresh tokens: Single-use or rotation (new refresh token with each refresh)
- Rate limiting on token endpoints (100 requests/hour per IP)
- IP binding for refresh tokens (optional for enhanced security)
### Scopes and Permissions
- `tenant:read`: Access to tenant data (events, photos, members)
> **Hinweis:** Der Wert von `VITE_OAUTH_CLIENT_ID` dient jetzt als alleinige Quelle der Wahrheit für den Tenant-Admin-OAuth-Client. Der Seeder `OAuthClientSeeder` greift auf `config/services.php` zu, das wiederum diesen Env-Wert ausliest und passende Redirect-URIs generiert (`/event-admin/auth/callback` für DEV und APP_URL). Stimmt der Wert im Frontend nicht mit dem Seeder überein, schlägt der PKCE-Login mit `invalid_client` fehl.
- Error scenarios (invalid code, expired tokens, state mismatch)
- Concurrent access testing
- Checkout login flow with pending_purchase
### Security Tests
- CSRF protection validation
- PKCE bypass attempts
- Token replay attacks
- Rate limiting enforcement
- Username/email ambiguity handling
## Deployment Considerations
### 1. Secrets Management
- Store JWT secret and OAuth client secret in secure vault (AWS Secrets Manager, HashiCorp Vault)
- Rotate secrets every 90 days
- Use different secrets for dev/staging/production
### 2. Certificate Management
- Use Let's Encrypt or commercial SSL certificates
- Rotate certificates before expiry
- Enable HSTS headers
### 3. Monitoring
- Track authentication success/failure rates
- Monitor token expiry patterns
- Alert on PKCE validation failures
- Log all security-related events
- Monitor checkout login success rates and identifier usage
This implementation provides secure, scalable authentication for the Fotospiel tenant system, following OAuth2 best practices with PKCE for public clients and flexible session auth for web flows.
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.
- **Wizard API surface** (JSON routes under `/checkout/*`) is session-authenticated, CSRF-protected, and returns structured payloads consumed by the PWA.
- **Webhooks** (Stripe, Paddle) map incoming provider events back to `CheckoutSession` rows to guarantee reconciliation and support 3DS / async capture paths.
- **Feature Flag**: `config/checkout.php` exposes `CHECKOUT_WIZARD_ENABLED` and `CHECKOUT_WIZARD_FLAG` so the SPA flow can be toggled or gradual-rolled out during launch.
- **Operational**: Rotate JWT signing keys with `php artisan oauth:rotate-keys`(updates key folder per KID; remember to bump `OAUTH_JWT_KID`).
- **Operational**: Track Sanctum PAT issuance via `personal_access_tokens`and document forced logout procedures (no OAuth key rotation required anymore).
## Payment State Machine
State constants live on `CheckoutSession` (`status` column, enum):
Diese Dokumentation beschreibt alle API-Endpunkte, die die Tenant Admin App mit der Backend-Hauptapp kommuniziert. Alle Requests sind tenant-scoped und erfordern JWT-Authentifizierung.
Diese Dokumentation beschreibt alle API-Endpunkte der Tenant Admin App. Alle Requests sind tenant-scoped und erfordern ein Sanctum Personal Access Token (PAT) mit der Fähigkeit `tenant-admin` bzw. `tenant:<id>`.
## Authentifizierung
### OAuth2 Flow (PKCE)
- **Start**: `GET /oauth/authorize` (Browser-Redirect mit PKCE-Challenge)
- **VITE_OAUTH_CLIENT_ID**: OAuth-Client-ID (Pflicht, muss mit `config/services.php` übereinstimmen – der Seeder legt damit den Client in `oauth_clients` an)
- Sanctum Personal Access Tokens (`/api/v1/tenant-auth/login|exchange`) mit Fähigkeiten `tenant-admin`, `tenant:{id}`; Speicherung in IndexedDB sowie Keychain/Keystore für Capacitor.
- Rollenbasierte Fähigkeiten:`tenant_admin` (vollständig),`member` (read-only, Upload). Token-Exchange nach Google-Login oder Session-Login via `/tenant-auth/exchange`.
### Onboarding Journey
- Routen `/event-admin/welcome/*` bilden den Flow.
@@ -67,7 +67,7 @@ Die App nutzt Endpunkte aus `docs/prp/03-api.md`.
- **Internationalisierung**: Sprachumschaltung in Einstellungen; Standard de, Fallback en.
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.