Разработана первая версия приложений
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# HAN Chat Keycloak
|
||||
|
||||
Production-like Keycloak 26.1.4 image and realm for OTP-only phone authentication. The module is self-contained and does not publish host ports; root nginx must proxy `/auth/*` to `keycloak:8080`.
|
||||
|
||||
## Security contract
|
||||
|
||||
- Realm `han-chat`; public client `han-chat-frontend`.
|
||||
- Authorization Code flow only, mandatory PKCE S256; implicit, password/direct, device and service-account grants are disabled.
|
||||
- Access tokens contain audience `han-chat-api`, canonical E.164 `phone_number` and boolean `phone_number_verified`.
|
||||
- Access token lifetime is 5 minutes. Refresh token rotation is enabled with max reuse `0`; SSO idle/max are 30/90 days.
|
||||
- Realm brute-force protection uses temporary bounded lockouts.
|
||||
- OTP challenges, send counters and security events are stored in provider-owned PostgreSQL tables in the Keycloak schema. Liquibase migration `han-otp-1.0.0` is applied by Keycloak's JPA entity provider.
|
||||
- OTP and phone values are never logged. Durable rate records use HMAC-SHA256 phone identifiers; challenge verification uses HMAC and constant-time comparison.
|
||||
- Settings are fetched only from `GET /internal/settings/v1/otp` with `Authorization: Bearer ${KEYCLOAK_SETTINGS_BRIDGE_TOKEN}`. ETag/cache and bounded last-known-good are supported; an empty or stale cache fails closed.
|
||||
- Mock mode is explicit. Startup rejects missing values, code `1234`, codes shorter than six characters, and HMAC keys shorter than 32 bytes. Disabling mock mode without a real delivery provider fails startup.
|
||||
|
||||
## Build and test
|
||||
|
||||
Requires Java 21 and Maven 3.9:
|
||||
|
||||
```bash
|
||||
mvn -B -ntp clean verify
|
||||
docker build -t han-chat/keycloak:26.1.4-otp-1.0.0 .
|
||||
```
|
||||
|
||||
The Maven build shades only libphonenumber into the provider JAR; Keycloak SPI dependencies remain provided by the pinned server image.
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy values from `.env.example` into the root backend `.env`; never commit `.env`. Generate independent random values for admin password, mock code, OTP HMAC key and settings bridge token.
|
||||
|
||||
Before production deployment replace the explicit placeholder entries in `realm/han-chat-realm.json`:
|
||||
|
||||
- `https://APP_LINK_HOST.example/auth/callback`
|
||||
- `https://APP_LINK_HOST.example/auth/logout`
|
||||
- `https://APP_WEB_ORIGIN.example`
|
||||
|
||||
Use exact Expo universal/app links and web origins. Do not replace them with wildcards. `https://tohin.ru/auth/callback` and `han-chat://auth/callback` are already allow-listed.
|
||||
|
||||
The JDBC URL must use the managed PostgreSQL private endpoint, TLS verification and `currentSchema=keycloak`. The database role must have privileges only on schema `keycloak`.
|
||||
|
||||
## Runtime
|
||||
|
||||
For standalone validation:
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env up --build
|
||||
```
|
||||
|
||||
The service exposes only Docker-network ports:
|
||||
|
||||
- application HTTP: `8080`, relative path `/auth`;
|
||||
- management health and metrics: `9000`;
|
||||
- readiness: `GET http://keycloak:9000/auth/health/ready`;
|
||||
- liveness: `GET http://keycloak:9000/auth/health/live`;
|
||||
- Prometheus metrics: `GET http://keycloak:9000/auth/metrics`.
|
||||
|
||||
Only nginx may publish external ports. Preserve `Host`, `X-Forwarded-Proto=https`, `X-Forwarded-Host`, `X-Forwarded-Port=443` and the trusted client IP chain.
|
||||
|
||||
## Realm lifecycle
|
||||
|
||||
`--import-realm` is suitable for a clean environment. It does not safely reconcile an existing production realm. For changes to a live realm:
|
||||
|
||||
1. take a managed PostgreSQL backup/PITR checkpoint and export the current realm without users/secrets;
|
||||
2. compare the desired safe subset (clients, scopes, flows, token policy);
|
||||
3. apply through a controlled admin job or Admin API procedure;
|
||||
4. verify discovery issuer, JWKS, PKCE login, refresh rotation and logout;
|
||||
5. retain old passive signing keys until all tokens signed by them expire.
|
||||
|
||||
Private signing keys are generated and stored by Keycloak and are absent from the realm JSON.
|
||||
|
||||
## OTP data and operations
|
||||
|
||||
Provider tables:
|
||||
|
||||
- `han_otp_challenge`: expiring, one-time challenges with optimistic version and pessimistic verification lock;
|
||||
- `han_otp_send_counter`: durable 24-hour counter/cooldown per phone HMAC;
|
||||
- `han_otp_security_event`: append-only minimal outcomes without raw phone or OTP.
|
||||
|
||||
Resend marks an earlier active challenge as superseded. Verification locks a challenge row, increments attempts, and atomically consumes a valid challenge, preventing replay and parallel double use.
|
||||
|
||||
Expired challenge and old security-event retention should be removed by a scheduled database maintenance job executed with the Keycloak schema role. Recommended retention is 24 hours for expired challenges/counters and the legally approved audit retention for security events. Cleanup must run in bounded batches and must not alter standard Keycloak tables.
|
||||
|
||||
## Release and recovery
|
||||
|
||||
Before upgrading Keycloak, read migration notes, rebuild the provider against the exact target SPI version, test on a database clone, and execute OTP login/refresh/logout contract tests. Do not skip major versions without a supported path.
|
||||
|
||||
Backups must include the full Keycloak schema (realm signing keys and provider tables). After restore verify issuer `https://tohin.ru/auth/realms/han-chat`, JWKS, client redirects, browser flow binding, challenge persistence and refresh revocation before opening traffic.
|
||||
Reference in New Issue
Block a user