# Auth Rules Use this document during the **Auth / Runtime / Realm** stage defined in `prompts/general-prompt.md`. ## Purpose Generate and preserve the auth contracts that let the CRUD app run as a React Admin SPA backed by a NestJS API protected by external Keycloak. ## Mandatory Inputs - `prompts/general-prompt.md` - `prompts/runtime-rules.md` - current repository auth/runtime defaults ## Expected Outputs - `client/src/auth/` - `client/src/dataProvider.ts` - `server/src/auth/` - `toir-realm.json` ## Frontend Auth Invariants - use `keycloak-js` with redirect-based login only - initialize Keycloak before rendering the SPA - use Authorization Code Flow + PKCE (`S256`) - keep `authProvider`, `dataProvider`, `getIdentity()`, `getPermissions()`, and `checkError()` as stable seams - derive identity from token claims already present in the token - do not call `loadUserProfile()` - `401` forces re-authentication; `403` remains an authorization error - keep token handling in memory with one shared in-flight refresh path ## Backend Auth Invariants - verify JWTs with `jose` - validate issuer, audience, and signature via JWKS - resolve JWKS in this order: 1. `KEYCLOAK_JWKS_URL` 2. OIDC discovery at `/.well-known/openid-configuration` 3. `${issuer}/protocol/openid-connect/certs` - extract roles only from `realm_access.roles` - keep `/health` public - generated CRUD routes stay protected by default ## Working Runtime Defaults Keep these defaults unless a task explicitly overrides them: - `VITE_KEYCLOAK_URL=https://sso.greact.ru` - `VITE_KEYCLOAK_REALM=toir` - `VITE_KEYCLOAK_CLIENT_ID=toir-frontend` - `KEYCLOAK_ISSUER_URL=https://sso.greact.ru/realms/toir` - `KEYCLOAK_AUDIENCE=toir-backend` - `CORS_ALLOWED_ORIGINS=http://localhost:5173,https://toir-frontend.greact.ru` Anti-regression rule: - do not revert shared examples to localhost Keycloak defaults unless a task explicitly requests a local Keycloak baseline ## Realm Artifact Contract The root realm artifact is mandatory and must: - be importable and versioned - align with generated frontend/backend env contracts - parameterize: - realm name - frontend client id - backend client id / audience - local and production frontend URLs - artifact filename - explicitly deliver: - `sub` - `aud` - `realm_access.roles` - define: - realm roles `admin`, `editor`, `viewer` - a public SPA client with PKCE S256 - a bearer-only backend client - an explicit audience client scope - protocol mappers for baseline identity and role claims ## Completion Expectations Auth/runtime generation is incomplete if any of the following is true: - frontend and backend auth seams drift from each other - JWKS resolution order changes - `/health` stops being public - shared Keycloak defaults regress to localhost examples - the realm artifact no longer matches backend/frontend expectations