keycloak init

This commit is contained in:
MaKarin
2026-03-21 16:00:27 +03:00
parent 33521016d3
commit 8d6875f4b0
50 changed files with 2242 additions and 252 deletions

120
auth/backend-auth-rules.md Normal file
View File

@@ -0,0 +1,120 @@
# Backend Auth Rules
This document defines mandatory backend authentication and authorization behavior for generated applications.
---
# Generated Backend Auth Surface
The generator must create explicit auth infrastructure in the generated NestJS backend.
At minimum generate:
- `server/src/auth/auth.module.ts`
- JWT auth guard
- roles guard
- `@Public()`
- `@Roles()`
- typed authenticated principal interface
- typed environment validation in `server/src/config/`
The generator must not describe backend auth as an external manual integration step.
---
# Standards-Based JWT Verification
The generated backend must validate JWTs against Keycloak using standards-based libraries.
Rules:
1. Verify **issuer**
2. Verify **audience**
3. Verify signature via **JWKS**
4. Do **not** use deprecated Keycloak-specific Node adapters such as `keycloak-connect`
The default library rule for this repository is:
- **`jose`** for JWT and JWKS verification
---
# JWT Verification Contract
The generated backend must verify tokens with:
- `KEYCLOAK_ISSUER_URL`
- `KEYCLOAK_AUDIENCE`
JWKS resolution priority must be exactly:
1. explicit `KEYCLOAK_JWKS_URL`
2. OIDC discovery
3. fallback `${issuer}/protocol/openid-connect/certs`
The generator must encode this priority explicitly.
---
# Role Extraction
Authorization roles must be extracted **only** from:
- `realm_access.roles`
The generator must not mix in:
- resource roles
- custom frontend-only permissions
- undocumented claim fallbacks
`realm_access.roles` is the single RBAC source for this repository.
---
# Default RBAC Policy
Apply these RBAC defaults to generated CRUD controllers:
- `GET`: `viewer`, `editor`, `admin`
- `POST`: `editor`, `admin`
- `PATCH`: `editor`, `admin`
- `PUT`: `editor`, `admin`
- `DELETE`: `admin`
`GET /health` must remain public and must use the generated `@Public()` mechanism.
All other generated CRUD routes must be protected by default.
---
# Typed Principal
The generated backend must attach a typed authenticated principal to the request context.
At minimum, the generated principal type must be able to represent:
- `sub`
- user identity fields when present
- `roles`
- raw claims payload
This principal type is required so guards, controllers, and tests share one consistent contract.
---
# Backend Environment Contract
The generated backend env contract must include:
- `PORT`
- `DATABASE_URL`
- `CORS_ALLOWED_ORIGINS`
- `KEYCLOAK_ISSUER_URL`
- `KEYCLOAK_AUDIENCE`
- `KEYCLOAK_JWKS_URL` (optional)
The generated backend config must **fail fast** if required auth variables are missing.
The generated backend must not silently fall back to production auth settings in code.

134
auth/frontend-auth-rules.md Normal file
View File

@@ -0,0 +1,134 @@
# Frontend Auth Rules
This document defines mandatory frontend authentication behavior for generated applications.
---
# Generated Files
The generator must create at minimum:
- `client/src/config/env.ts`
- `client/src/auth/keycloak.ts`
- `client/src/auth/authProvider.ts`
- `client/src/App.tsx`
- `client/src/main.tsx`
- `client/src/dataProvider.ts`
- `client/.env.example`
`App.tsx`, `main.tsx`, and `dataProvider.ts` must be generated in an auth-aware form. Auth must not be bolted on later.
---
# Login Model
The generated frontend must use:
- **Keycloak JS**
- **Authorization Code Flow + PKCE**
- **PKCE method `S256`**
- **redirect-based login only**
The generator must **not** create a custom in-app username/password login form.
The generated SPA must initialize Keycloak **before rendering** the application. The app must not operate anonymously once auth is enabled.
---
# React Admin Integration
The generated frontend must use a React Admin **`authProvider`** and connect it at the `Admin` root.
Rules:
1. `authProvider` is mandatory.
2. The generated `Admin` root must enforce authenticated operation.
3. Auth bootstrap must happen before rendering `Admin`.
The generated frontend must not rely on anonymous access with later lazy auth attachment.
---
# Shared Request Seam
The generated frontend must use the shared request seam in `client/src/dataProvider.ts` as the single place where access tokens are attached.
Rules:
1. All backend requests must carry `Authorization: Bearer <access_token>`.
2. This must cover all React Admin calls, including:
- list
- get one
- get many
- get many reference
- create
- update
- delete
3. Reference/resource lookups must flow through the same authenticated request seam.
The generator must not scatter token attachment across resource components.
---
# Error Semantics
The generated `authProvider.checkError` must distinguish authentication failures from authorization failures:
- `401` -> force logout / re-authentication
- `403` -> do **not** re-authenticate; surface access denied / permission error to React Admin
The generator must not treat `401` and `403` as equivalent.
---
# Token Refresh
The generated frontend must refresh tokens before protected API calls when needed.
Refresh behavior must be **concurrency-safe**:
- use one shared in-flight refresh operation
- parallel requests must wait for the same refresh promise
- do not trigger multiple parallel refresh requests for the same expiry window
The generator must explicitly describe or implement the shared in-flight refresh pattern.
---
# Browser Storage Rules
The generated frontend must **not** store access tokens or refresh tokens in:
- `localStorage`
- `sessionStorage`
In-memory handling via Keycloak JS behavior is the default rule for this repository.
---
# Frontend Environment Contract
The generated frontend env contract must include:
- `VITE_API_URL`
- `VITE_KEYCLOAK_URL`
- `VITE_KEYCLOAK_REALM`
- `VITE_KEYCLOAK_CLIENT_ID`
The generated frontend config module must **fail fast** if required auth variables are missing.
The generated frontend must not silently fall back to production auth settings in code.
---
# Default Values for Examples
`client/.env.example` must use these repository defaults as examples:
```env
VITE_API_URL=http://localhost:3000
VITE_KEYCLOAK_URL=https://sso.greact.ru
VITE_KEYCLOAK_REALM=toir
VITE_KEYCLOAK_CLIENT_ID=toir-frontend
```

View File

@@ -0,0 +1,89 @@
# Keycloak Architecture
This repository generates a fullstack CRUD application with **default Keycloak authentication and authorization**. Authentication is not an optional add-on and must not be described as a manual post-generation task.
---
# Default Auth Model
The generated application must use this runtime topology:
- **Frontend:** SPA served by Vite + React Admin
- **Backend:** NestJS API
- **Auth transport:** `Authorization: Bearer <access_token>`
- **Identity provider:** external Keycloak server
The generated application must **not** use:
- a BFF layer
- cookie/session authentication
- a custom in-app username/password login form
The generated application must use **redirect-based Keycloak login** only.
---
# Auth Is Part of Default Generation
The generator must produce:
- authenticated frontend bootstrap and request flow
- authenticated backend bootstrap and request guards
- auth-aware env examples
- auth-aware validation rules
- a root-level Keycloak realm import artifact that describes the Keycloak realm/client bootstrap
Repository defaults may use names such as `toir`, `toir-frontend`, `toir-backend`, and `toir-realm.json`, but future generations must parameterize realm name, client IDs, production URLs, and artifact filename from the generated project or explicit auth configuration.
The generated application must not require a separate prompt step such as "now add auth manually".
---
# Public and Protected Routes
The generated backend must keep:
- `GET /health` — public
All generated CRUD routes must be protected by default.
Authorization must be role-based and must use **only** Keycloak realm roles from `realm_access.roles`.
---
# Default Role Policy
Apply these RBAC defaults to generated CRUD controllers:
- `GET` list/detail: `viewer`, `editor`, `admin`
- `POST`, `PATCH`, `PUT`: `editor`, `admin`
- `DELETE`: `admin`
The frontend may use roles for display and permission awareness, but the backend remains the enforcement point.
---
# Token Contract
Generated auth context must guarantee that access tokens used by the SPA and API reliably contain:
- `sub`
- `aud`
- `realm_access.roles`
The frontend client and backend audience/client must be documented explicitly. The generator must not rely on undocumented Keycloak defaults for these claims.
---
# Runtime Boundaries
The repository's local runtime topology remains unchanged:
- `docker-compose.yml` provisions PostgreSQL only
- Keycloak remains external to this repo's Docker runtime
The generator must therefore produce:
- runtime documentation for importing/configuring the generated realm import artifact
- env contracts for frontend and backend
- validation rules that assume an external but reachable Keycloak server

View File

@@ -0,0 +1,152 @@
# Keycloak Realm Template Rules
This document defines the required Keycloak realm/bootstrap artifact that the generator must produce.
---
# Required Artifact
The generator must create a root-level Keycloak import artifact:
- project-specific realm import artifact
- repository default example filename: `toir-realm.json`
This artifact is part of the normal generated result and must be documented in the runtime bootstrap flow.
The generator must parameterize at minimum:
- realm name
- frontend SPA client ID
- backend audience/resource client ID
- production frontend URLs
- realm-artifact filename
Repository defaults may use `toir`, `toir-frontend`, `toir-backend`, `toir-realm.json`, and `https://toir-frontend.greact.ru` as examples, but those names must not be treated as universal requirements for all future generated applications.
---
# Strategy Choice
This repository uses the **robust bootstrap strategy** by default.
That means the generated realm/client setup must be:
- self-contained
- reproducible after import
- explicit about audience delivery
- explicit about role delivery
- explicit about required claims
The generator must **not** rely on built-in client scopes magically existing and attaching correctly after realm import.
---
# Required Realm Structure
The generated realm import artifact must define:
- realm name derived from generated project auth config
- realm roles:
- `admin`
- `editor`
- `viewer`
- frontend SPA client ID derived from generated project auth config
- backend audience/resource client ID derived from generated project auth config
- dedicated audience client scope for audience delivery
- repository default example: `api-audience`
The audience client scope must explicitly add the generated backend audience/client ID to SPA access tokens.
---
# Required Frontend Client Rules
The generated SPA client must be configured as a public SPA client with:
- `publicClient: true`
- `standardFlowEnabled: true`
- `implicitFlowEnabled: false`
- `directAccessGrantsEnabled: false`
- `serviceAccountsEnabled: false`
- `pkce.code.challenge.method: S256`
Default rule:
- keep `fullScopeAllowed: true`
Reason:
- this repository uses realm-role RBAC
- earlier failures came from fragile or implicit scope behavior
- the robust default is explicit audience delivery plus explicit claim mappers, not a partially documented scoped-role setup
If a future prompt chooses `fullScopeAllowed: false`, it must also fully document role scope mappings and validation rules. The generator must not switch to that strategy silently.
---
# Required Claim Delivery
The generated realm/client configuration must reliably produce access tokens containing:
- `sub`
- `aud`
- `realm_access.roles`
The generator must explicitly address claim delivery and must not leave it implicit.
The generated realm import artifact must therefore define explicit protocol mappers for:
- `sub`
- user identity claims needed by the frontend
- `realm_access.roles`
The generator must not assume these claims will appear through undeclared built-in scopes.
Post-generation validation must fail if the generated realm contract leaves `sub`, `aud`, or `realm_access.roles` implicit instead of explicitly delivered.
---
# Required Role Delivery
The generated realm/client configuration must explicitly deliver realm roles into the access token.
Rules:
1. Role delivery must be configured intentionally.
2. `realm_access.roles` must be present in the access token after import.
3. The generator must validate that realm role delivery is part of the generated realm artifact.
---
# Redirect URIs and Web Origins
The generated realm template guidance must document local and production frontend URLs derived from the generated project configuration.
Repository default production examples may include:
- `http://localhost:5173`
- `https://toir-frontend.greact.ru`
The generated SPA client must include matching redirect URIs and web origins for the generated local and production frontend URLs. These values must be explicit in the generated realm artifact or in a generated artifact template with explicit placeholders and validation instructions.
---
# Anti-Regression Rule
The earlier broken realm-template behavior must not be reproduced.
The generator context must explicitly prevent:
- missing audience in SPA access tokens
- missing `sub`
- missing `realm_access.roles`
- realm imports that depend on undeclared built-in scopes being present
- ambiguous role-delivery behavior
The generated realm/bootstrap guidance must be self-contained enough that another engineer can import the artifact and predict the access-token shape without relying on hidden Keycloak defaults.
The generator must guarantee through the generated realm artifact plus post-generation validation that imported access tokens reliably contain:
- `sub`
- `aud`
- `realm_access.roles`