40227-vm/frontend/docs/auth-integration.md
2026-06-12 06:55:35 +02:00

94 lines
6.0 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.

# Auth Integration
## Purpose
The product frontend authenticates through backend-owned session cookies.
The backend transport details are documented in `backend/docs/cookie-auth.md`.
## Runtime Configuration
The frontend reads only public browser-safe API configuration from:
- `VITE_BACKEND_API_URL`
Local values live in ignored `frontend/.env` files; the template value is documented in `frontend/.env.example`.
Do not add secrets to frontend env files. Vite exposes `VITE_*` values to the browser bundle.
## Auth Flow
1. `AuthContext` delegates auth state to `useAuthSession` from `frontend/src/business/auth/hooks.ts`.
2. `useAuthSession.signIn` calls `POST /api/auth/signin/local` through `frontend/src/shared/api/auth.ts`.
3. The backend sets an HttpOnly auth cookie and returns the current user profile.
4. `useAuthSession` restores the session with `GET /api/auth/me`.
5. The backend returns the current product profile, including `app_role` (`{ id, name, scope, globalAccess }`), `campus`, `staffProfile`, and `permissions`. The UI role is derived from `app_role.name` (one of the 11 role names); there is no separate `productRole`.
6. UI-facing `StaffProfile` is derived in `frontend/src/business/auth/mappers.ts`.
7. `useAuthSession.signOut` calls `POST /api/auth/signout`; the backend clears the auth cookie.
8. `SignInModal` delegates modal mode, form draft state, validation, and submit workflow to `useAuthModalWorkflow`.
## Refresh Tokens
The refresh flow keeps tokens backend-owned:
1. Backend sign-in sets short-lived access and long-lived refresh HttpOnly cookies.
2. Protected API requests use the access cookie only.
3. `POST /api/auth/refresh` uses the refresh cookie only, rotates it server-side, sets fresh cookies, and returns the current user profile.
4. `httpClient` performs one controlled refresh-and-retry after an access-expiry `401`. A `403` is treated as **forbidden, not expired** — it surfaces as an `ApiError` (no refresh, no logout) and a global `QueryCache`/`MutationCache` handler toasts it.
5. If refresh fails because both access and refresh credentials are expired or invalid, the business layer clears the user and redirects to `/login`.
6. Non-auth backend failures remain observable errors; no infinite retry and no silent fallback.
The frontend must not read, store, or receive access or refresh token values.
## Login Route
The app exposes `/login` as the deterministic destination for expired sessions and for unauthenticated visitors. An `AuthGuard` (`frontend/src/app/AuthGuard.tsx`) wraps the shell and redirects unauthenticated users to `/login`; the shell is authenticated-only. The previous in-shell guest-preview experience (guest banner, guest role picker, in-shell sign-in) was removed — sign-in happens on `/login` via the retained `SignInModal`.
Rules:
- `/login` must reuse the existing auth business hook and API functions.
- Auth-expired redirects must use `replace: true`.
- Safe return paths may be preserved only for same-origin product routes.
- Tokens and raw session failure details must never be placed in the URL.
- Expired access plus valid refresh must restore the session without redirecting.
- Expired access plus expired refresh must redirect to `/login` without showing a raw error.
## Authorization (frontend gating)
Authorization is UX-only on the frontend; the backend remains the sole authority. The user's effective permissions arrive on `CurrentUser.permissions` (role permissions `custom_permissions`).
- **Permission vocabulary:** `frontend/src/shared/auth/permissions.ts` — typed `${VERB}_${ENTITY}` + product-feature permission names mirroring the backend.
- **Selectors + hook:** `frontend/src/business/auth/permissions.ts` (`hasPermission`/`hasAnyPermission`/`hasAllPermissions`) and the `usePermissions()` hook (`frontend/src/hooks/usePermissions.ts`). All are `globalAccess`-aware: the two system roles (`super_admin`/`system_admin`) carry no permission rows but pass every check, mirroring the backend bypass.
- **Affordance gate:** `<PermissionGate permission|anyOf|allOf>` (`frontend/src/components/auth/PermissionGate.tsx`) hides children the user lacks permission for.
- **Route gating:** module/route visibility is role-based via `MODULES[].roles` (it matches the backend permission matrix). `ModuleRouteGuard` renders the 404 page for a forbidden direct URL; `IndexRedirect` lands each role on its first accessible module (so e.g. students/guardians land on the external pages, not the dashboard).
## Layering
- View/provider: `frontend/src/contexts/AuthContext.tsx`, `frontend/src/components/frameworks/SignInModal.tsx`, and `frontend/src/components/sign-in-modal/`
- Business logic: `frontend/src/business/auth/`
- API/data access: `frontend/src/shared/api/auth.ts`
- Backend contract types: `frontend/src/shared/types/auth.ts`
## Deferred Product Onboarding
Registration, company creation, campus creation, user creation, staff profile creation, and profile updates are intentionally deferred.
The backend has generated auth signup/profile endpoints, but the customer has not approved the product workflow for creating companies, campuses, users, role assignments, campus assignments, and staff profiles.
Rules:
- Do not implement frontend registration or profile creation flows until the backend product contract is defined.
- Do not treat generated auth signup/profile endpoints as the product onboarding contract.
- Track the cross-application onboarding task in `docs/backlog.md`.
- New persisted workflows must use typed backend API modules and business-layer hooks.
## Standards
- Do not duplicate backend role mapping in the frontend.
- Do not add frontend secrets.
- Do not store auth tokens in frontend browser storage.
- Do not expose auth tokens in URLs or API responses.
- Do not add frontend refresh-token storage; refresh uses only backend-owned HttpOnly cookies.
- New persisted workflows must use `frontend/src/shared/api/` through `frontend/src/business/<module>/`.
- New server state should use business-layer hooks built on top of shared API modules.