diff --git a/.cursor/rules/102_multi_tenancy.mdc b/.cursor/rules/102_multi_tenancy.mdc index 55ca8961..5e9b5f0f 100644 --- a/.cursor/rules/102_multi_tenancy.mdc +++ b/.cursor/rules/102_multi_tenancy.mdc @@ -1,6 +1,6 @@ --- description: Multi-tenancy and portal architecture rules for Crewli -globs: ["api/**/*.php", "apps/portal/**/*.{vue,ts}"] +globs: ["api/**/*.php"] alwaysApply: true --- @@ -92,16 +92,20 @@ Route::middleware(['auth:sanctum', 'event.role:event_manager'])->group(...); ## Portal Architecture -### Two Access Modes in One App (`apps/portal/`) +### Two Access Modes Under `/portal/*` Routes (within `apps/app/`) + +Post-WS-3, the portal lives in the main SPA at `/portal/*` routes. +Two access modes coexist: | Mode | Middleware | Users | Token Source | -|------|-----------|-------|-------------| -| Login | `auth:sanctum` | Volunteers, Crew | Bearer token from login | +|------|------------|-------|--------------| +| Login | `auth:sanctum` | Volunteers, Crew | Bearer token from login (httpOnly cookie) | | Token | `portal.token` | Artists, Suppliers, Press | URL token param: `?token=ULID` | ### Token-Based Authentication Flow ``` -1. Artist/supplier receives email with link: https://portal.crewli.app/advance?token=01HQ3K... +1. Artist/supplier receives email with link: https://crewli.app/portal/advance?token=01HQ3K... + (Legacy portal.crewli.app links 301-redirect, preserving the token query param) 2. Portal detects token in URL query parameter 3. POST /api/v1/portal/token-auth { token: '01HQ3K...' } 4. Backend validates token against artists.portal_token or production_requests.token @@ -111,9 +115,9 @@ Route::middleware(['auth:sanctum', 'event.role:event_manager'])->group(...); ### Login-Based Authentication Flow ``` -1. Volunteer navigates to https://portal.crewli.app/login +1. Volunteer navigates to https://crewli.app/login 2. Enters email + password -3. POST /api/v1/auth/login (same endpoint as apps/app/) +3. POST /api/v1/auth/login 4. Returns user + organisations + event roles 5. Portal shows volunteer-specific views (My Shifts, Claim Shifts, Messages, Profile) ``` @@ -190,12 +194,11 @@ class PortalTokenMiddleware // config/cors.php 'allowed_origins' => [ env('FRONTEND_APP_URL', 'http://localhost:5174'), - env('FRONTEND_PORTAL_URL', 'http://localhost:5175'), ], 'supports_credentials' => true, ``` -Production example (subdomains on **crewli.app**): `FRONTEND_APP_URL=https://crewli.app`, `FRONTEND_PORTAL_URL=https://portal.crewli.app`, and `SANCTUM_STATEFUL_DOMAINS=crewli.app,portal.crewli.app`. +Production example (registered domain **crewli.app**): `FRONTEND_APP_URL=https://crewli.app` and `SANCTUM_STATEFUL_DOMAINS=crewli.app`. The legacy `FRONTEND_PORTAL_URL` env key is retained for outbound-email controllers (per AUTH_ARCHITECTURE.md ยง11), but resolves to the same host post-WS-3. ## Shift Claiming & Approval Flow