34 KiB
module-08. Проектная спецификация keycloak
Статус: целевая production-спецификация MVP; реальный SMS provider не входит в scope.
Источники:README.md,arch-00-glossary.md,arch-01-system-architecture.md,arch-02-api-contracts.md,arch-03-docker-compose-blueprint.md,arch-04-settings-and-content.md,arch-05-agent-development-process.md,module-01-api-backend.md,module-02-frontend-test-site.md,module-03-nginx.md,../../HAN_chat/deploy/init-managed-postgres.py.
1. Назначение и границы
Keycloak — единственный IdP HAN Chat. MVP предоставляет регистрацию/вход только по подтверждённому номеру телефона и OTP, OIDC tokens, refresh/logout, discovery/JWKS и защиту auth flow.
Keycloak отвечает за:
- realm, users, credentials, auth sessions и token lifecycle;
- Authorization Code Flow with PKCE для Expo web/iOS/Android;
- нормализацию/уникальность телефона и claims;
- OTP authenticator/SPI, mock verification и продуктовые limits;
- brute-force, sessions, logout/revocation;
- keys/JWKS rotation и health/metrics.
Не отвечает за:
api-backendbootstrap/consents/UserIdentity;- App DB/profile/chat и CRM sync;
- API service-to-service tokens;
- пользовательскую UX-сессию;
- реальную отправку SMS в MVP.
Реальный SMS provider — строго extension point/TBD. Mock code является секретом окружения, не контентом UI и не логируется.
2. Топология и публичный URL
Keycloak работает за единственным root nginx:
Client HTTPS https://tohin.ru/auth/*
→ nginx TLS termination
→ HTTP keycloak:8080 в закрытой Docker network
→ managed PostgreSQL schema keycloak по TLS
Публичный issuer обязан быть стабильным:
https://tohin.ru/auth/realms/han-chat
OIDC discovery:
https://tohin.ru/auth/realms/han-chat/.well-known/openid-configuration
JWKS — URI из discovery. api-backend проверяет iss, audience, signature, exp/nbf и sub, не вызывает Admin API в hot path.
3. Версия, image и providers
- Keycloak Quarkus distribution, поддерживаемая LTS/stable версия, закреплённая image digest.
- PostgreSQL JDBC driver из image.
- Custom Java provider JAR для phone OTP authenticator/settings bridge/counters.
- Сборка provider reproducible, зависимости pinned, SBOM/signature/security scan.
- Build-stage выполняет
kc.sh build; runtime image immutable/non-root. - Перед upgrade читаются Keycloak migration notes и SPI compatibility.
Версия Keycloak фиксируется в deployment manifest; latest запрещён.
4. Realm и clients
Realm: han-chat. Master realm не используется приложением.
4.1. Public frontend client
Канонический client id:
han-chat-frontend
Настройки:
- public client; client authentication off;
- standard flow on;
- Authorization Code + PKCE
S256обязательно; - implicit flow off;
- direct access grants/password grant off;
- service accounts off;
- device flow off, если не нужен;
- consent screen Keycloak не заменяет продуктовые согласия API;
- exact redirect URIs и web origins;
- full scope allowed off; только назначенные scopes/mappers.
Примеры redirect URI должны перечисляться отдельно:
https://tohin.ru/auth/callback
han-chat://auth/callback
<Expo native scheme/callback, exact value после сборки>
Production redirect URI задаются exact; wildcard не используется до отдельного security review. Development localhost origins/redirects находятся в отдельном dev realm/client либо profile и запрещены production.
4.2. API audience
Audience:
han-chat-api
Client scope/audience mapper добавляет aud=han-chat-api в access token frontend. api-backend не принимает token только по azp без audience.
4.3. Optional confidential client
han-chat-backend можно импортировать disabled/optional для будущих admin/ops S2S:
- client authentication on, service account only при явном включении;
- secret не хранится в realm export;
- минимальные roles;
- не используется между текущими сервисами и не требуется для JWT validation;
- не участвует в пользовательском hot path.
Internal API по-прежнему используют service tokens из arch-02.
5. OTP-only phone flow
5.1. Browser flow
Отдельный flow han-phone-otp-browser:
- Cookie/SSO authenticator проверяет действующую Keycloak session.
- При отсутствии session показывается форма телефона.
Phone Identity Authenticatorнормализует номер.- Проверяются realm brute-force и product send limits.
- Создаётся/находится user по canonical phone identity.
Phone OTP Challengeинициирует mock/provider send.- Показывается форма OTP.
- Проверяются TTL/attempt limits/constant-time hash or mock compare.
- При успехе user enabled/phone verified, flow завершается code.
- Frontend меняет code+verifier на tokens.
Password form, registration password, reset password, email OTP, social login и magic link отсутствуют.
5.2. Регистрация/find-or-create
До выдачи OTP новый user может существовать как short-lived pending identity либо создаваться после успешной проверки. Предпочтительное решение:
- normalized phone reservation/counter создаётся в SPI store;
- permanent Keycloak user создаётся/активируется только после успешного OTP;
- concurrent flow защищён unique phone index/transaction;
- abandoned pending challenges очищаются TTL.
Если Keycloak storage не позволяет безопасный custom unique index в managed schema, user создаётся disabled с deterministic username и очищается job; точная реализация покрывается concurrency tests.
5.3. Required actions
Используются только при реальной необходимости:
VERIFY_PHONE— если user импортирован/номер изменён вне текущего verified flow;UPDATE_PHONE— будущий controlled flow с повторной OTP;- terms/product consents не required action: версии и факт согласия хранит
api-backend.
Required actions не должны предлагать пароль/email. После phone OTP обычный вход завершается без лишнего profile screen.
6. Нормализация и уникальная identity
Телефон парсится libphonenumber:
- Unicode digits/NFKC input normalization;
- default region
RUдопустим только для национального ввода; международные номера поддерживаются по product policy; - canonical storage/claim — E.164, например
+79001234567; - invalid/impossible number отклоняется до send;
- отображение только masked;
- canonical phone comparison exact.
Рекомендуемая модель:
username= canonical E.164 либо irreversible deterministic identifier;- user attribute
phone_number= E.164; phone_number_verified=true;- unique phone enforced storage-level, не только pre-check;
- email nullable/не используется.
Утечка существования номера запрещена: initiate/challenge возвращают одинаковый внешний текст/timing class для нового/существующего пользователя. Один verified phone соответствует одному active sub. Merge/reassignment — отдельная administrative policy, не автоматический side effect login.
Изменение телефона требует re-auth + OTP нового номера и invalidation sessions/tokens по policy. api-backend обновляет cached phone при следующем bootstrap/login claim; прямого вызова Keycloak DB нет.
7. Mock OTP
Env:
KEYCLOAK_OTP_MOCK_ENABLED=true
KEYCLOAK_OTP_MOCK_CODE=<secret>
Правила:
- mock разрешён MVP production-like только как явно принятый риск;
- пустой/default
1234запрещён startup policy для production-like, если не согласован secret; - code не входит в realm import, frontend config, HTML hint, API response, logs, metrics, traces или audit;
- сравнение constant-time;
- challenge всё равно имеет TTL, max verify attempts и counters, чтобы flow был близок production;
- code не сохраняется per-user в открытом виде;
- UI сообщает только «тестовый режим», без кода;
KEYCLOAK_OTP_MOCK_ENABLED=falseпри отсутствии configured provider делает OTP flow fail-closed/not-ready, а не пропускает проверку.
Реальный provider interface:
interface OtpDeliveryProvider {
DeliveryResult send(E164Phone phone, String otp, Duration ttl, Correlation ctx);
}
Будущий provider обязан вернуть provider_message_id; raw OTP не логируется. Выбор provider, template, sender, delivery status webhook и vendor credentials — TBD.
8. OTP challenge и counters
Даже в mock:
- challenge id random ≥128 bit;
- OTP не хранится raw; production-generated code — keyed hash/HMAC с challenge salt/pepper;
- TTL (предлагается 5 минут) — technical security parameter;
- one-time use; success atomically consumes challenge;
- max verification attempts per challenge;
- resend invalidates либо version-binds предыдущий challenge;
- replay/parallel verify безопасны;
- destination stored masked/hash where possible.
Audit fields по arch-05: provider message id (для mock — synthetic non-secret), sent_at, destination_masked, otp_hash/reference, attempts, outcome. Никогда raw code.
8.1. Product send limits bridge
SPI вызывает:
GET http://api-backend:8000/internal/settings/v1/otp
Authorization: Bearer ${KEYCLOAK_SETTINGS_BRIDGE_TOKEN}
Ответ:
{
"max_send_attempts_per_24h": 3,
"min_seconds_between_attempts": 30,
"version": "2026-07-10T08:00:00Z",
"cache_ttl_seconds": 60
}
Это единственный путь к otp.phone.*; Keycloak не получает GRANT на han_app. SPI поддерживает ETag/cache, single-flight refresh. Bridge down:
- использовать last-known-good до bounded max stale;
- если cache пуст/слишком стар — fail-closed для send;
- verify уже выданного challenge может продолжаться по snapshot, с которым challenge создан.
Token name точно KEYCLOAK_SETTINGS_BRIDGE_TOKEN, endpoint точно /internal/settings/v1/otp.
8.2. Где хранятся counters
Решение MVP: counters/challenges хранятся в Keycloak-owned PostgreSQL tables/provider storage в схеме keycloak, а не в Redis API и не в han_app.
Причины:
- durable across restart;
- одна transaction для reserve/send-attempt/consume;
- не добавляет Keycloak credentials к общему Redis;
- соответствует границе «счётчики в зоне Keycloak/SPI».
Используются phone HMAC, не E.164 в key/index для rate data. Tables provider-owned создаются versioned migration provider-а, не ручным DDL-on-start.
Минимальные records:
han_otp_challenge: id, phone_hmac, otp_hash/mock marker, created/expires/consumed, verify attempts, settings version, provider id/status;han_otp_send_counter: phone_hmac, window_start, count, last_sent_at;han_otp_security_event: append-only minimal outcome/retention.
Indexes: unique active challenge policy, (phone_hmac,window_start), (expires_at). Cleanup bounded job. Доступ только keycloak_user.
9. Brute-force и abuse
Слои:
- nginx
/authIP rate limit (NGINX_RATE_LIMIT_AUTH); - Keycloak realm brute-force detection;
- SPI product send limits per phone HMAC;
- verify-attempt limit per challenge/phone/IP hash;
- cooldown after repeated failures;
- CAPTCHA/risk engine — future extension.
Realm включает brute-force protection с temporary lockout и bounded wait. Permanent lockout для consumer phone login без recovery runbook нежелателен. Error messages не различают unknown phone/wrong code/locked account сверх безопасной UX причины. Retry-After/remaining time выдаётся только если не помогает enumeration.
IP берётся только из trusted proxy chain; Keycloak настроен доверять forwarded headers от root nginx.
10. Claims и token contract
Access token минимум:
| Claim | Значение |
|---|---|
iss |
https://tohin.ru/auth/realms/han-chat |
sub |
immutable Keycloak user id |
aud |
включает han-chat-api |
azp |
han-chat-frontend |
exp, iat, nbf |
стандартные |
sid |
session id, если поддерживается |
auth_time |
время auth |
acr/amr |
отражает phone OTP |
phone_number |
canonical E.164 |
phone_number_verified |
true |
scope |
only allowed scopes |
preferred_username может совпадать с phone для compatibility, но канонический claim API — phone_number; module-01 допускает fallback только если E.164.
ID token предназначен client login state; API принимает access token, не ID token. Refresh token непрозрачен для приложения и хранится frontend secure storage.
PII minimization: full phone нужен API bootstrap по зафиксированному контракту, но не добавляется в service tokens/metrics/logs. Roles/groups выдаются только если используются authorization policy.
11. Signing keys, JWKS и rotation
- asymmetric signing, RS256 MVP;
none/HS algorithms запрещены; - active signing key + passive previous keys до истечения всех выпущенных tokens/grace;
- keys генерируются/хранятся Keycloak, private material не в realm export/repo;
- JWKS публичен через issuer;
- rotation rehearsed;
kidменяется, API controlled-refresh cache; - emergency compromise: disable key, revoke sessions, force re-login, alert/runbook;
- backup/restore учитывает realm keys.
Rotation interval и HSM/keystore — ops TBD. Изменение algorithm требует совместного rollout API verifier.
12. Token и session lifecycle
Предлагаемые MVP значения, окончательно принять security/product review:
- access token lifespan: 5 минут;
- SSO session idle: 30 дней;
- SSO session max: 90 дней;
- refresh token следует session limits;
- authorization code: 1 минута;
- login action: 5 минут;
- client session idle/max согласованы с SSO;
- clock skew минимальный.
Refresh:
- revoke refresh token on use / refresh token rotation включены;
- max reuse
0или минимально поддерживаемое значение; - frontend применяет single-flight, поэтому parallel refresh не требуется;
- reuse старого refresh token →
invalid_grant, возможная session revocation/security event; - offline tokens не выдаются.
Access token не хранится server-side и живёт до exp; критическая блокировка пользователя сопровождается logout/revocation/not-before policy.
13. Logout, revocation и browser cookies
Frontend вызывает OIDC end-session/logout с valid post-logout redirect, затем всегда очищает local tokens. Back-channel logout можно включить для clients, которые его поддержат; API JWT hot path не хранит browser session.
Cookies Keycloak:
Secure,HttpOnly;- SameSite согласно redirect/iframe requirements, по умолчанию
Lax; - domain/path минимальны (
/auth/host); - third-party cookie dependency не закладывается;
- session fixation предотвращается Keycloak;
- admin console cookies не расширяются на frontend origins.
Front-channel iframe checks не должны заставлять ослабить CSP всего сайта. Native logout использует system browser и app-link/custom scheme validation.
14. CORS, origins и redirects
- exact
Web Origins:https://tohin.ru; - no wildcard
*with credentials; - native apps не получают произвольные web origins;
- valid redirects exact/safely scoped;
- redirect URI comparison не допускает open redirect;
- post-logout redirects отдельно allow-listed;
- nginx и Keycloak CORS не должны дублировать противоречащие headers;
- token endpoint используется PKCE client без client secret;
- admin endpoints не CORS-доступны приложению.
Любой новый environment имеет отдельный host/client config, а не production wildcard.
15. Reverse proxy и hostname
Ключевые настройки (точные CLI names проверяются по закреплённой версии):
KC_HTTP_ENABLED=true
KC_HTTP_PORT=8080
KC_PROXY_HEADERS=xforwarded
KC_HOSTNAME=https://tohin.ru/auth
KC_HTTP_RELATIVE_PATH=/auth
KC_HOSTNAME_STRICT=true
KC_HOSTNAME_STRICT_HTTPS=true
Если выбран другой поддержанный pattern (hostname без path + relative path), итоговые issuer/endpoints обязаны совпасть с KEYCLOAK_PUBLIC_URL.
Nginx передаёт trusted Host, X-Forwarded-Proto=https, X-Forwarded-Host, X-Forwarded-Port=443, real IP. Keycloak не доступен напрямую с host/public network, поэтому spoofed forwarded headers не принимаются извне.
Admin hostname/path рекомендуется ограничить ops network/VPN; публично нужны только realm/OIDC/login assets. Если разделить admin hostname невозможно MVP, admin console защищается network allow-list и сильным admin auth.
16. PostgreSQL keycloak
Используется:
KEYCLOAK_DB_URL=jdbc:postgresql://.../han_chat?...¤tSchema=keycloak
KC_DB_URL_PROPERTIES=currentSchema=keycloak
Role keycloak_user имеет доступ только к schema keycloak; нет доступа han_app, bitrix_*, message_safety. Connection только private VPC + TLS verify.
Pool:
- bounded initial/min/max;
- acquisition/query/connect timeout;
- leak detection/metrics;
- pool max определяется load test и managed PG limit;
application_name=keycloak.
Keycloak управляет своей стандартной schema migration. Custom provider tables имеют отдельную versioned migration strategy, совместимую с startup/rolling upgrade; DDL не выполняется бесконтрольно каждым replica.
Нельзя редактировать стандартные Keycloak tables вручную или Alembic-миграциями Python-сервисов.
17. Admin bootstrap и realm import
17.1. Bootstrap admin
KC_BOOTSTRAP_ADMIN_USERNAME/password или актуальный bootstrap mechanism только на первом запуске;- password генерируется strong secret, не коммитится и после bootstrap ротируется/удаляется из runtime env;
- admin user не используется приложением;
- отдельные named admin accounts/least privilege для ops;
- MFA для admin обязательно до production, независимо от consumer phone flow;
- admin events audit включён.
17.2. Realm import
Репозиторий:
keycloak/
realm/han-chat-realm.json.template
providers/han-phone-otp-provider.jar
themes/han-phone/
migrations/
scripts/{render-realm,validate-realm,export-realm}.sh
tests/
Dockerfile
docker-compose.yml
Export/template содержит realm/client/flow/scopes/policies, но не:
- client/admin/provider secrets;
- mock code;
- private signing keys;
- environment-specific production credentials.
Import автоматически допустим для clean local/test. Production changes применяются controlled declarative job/Admin API procedure с diff/backup, не --import-realm поверх живого realm без проверки. Drift detection сравнивает безопасный desired subset.
18. Settings и secrets
Канонические:
KEYCLOAK_PUBLIC_URL=https://tohin.ru/auth
KEYCLOAK_INTERNAL_URL=http://keycloak:8080
KEYCLOAK_REALM=han-chat
KEYCLOAK_AUDIENCE=han-chat-api
KEYCLOAK_DB_URL=jdbc:postgresql://...
KC_DB_URL_PROPERTIES=currentSchema=keycloak
KEYCLOAK_OTP_MOCK_ENABLED=true
KEYCLOAK_OTP_MOCK_CODE=<secret>
KEYCLOAK_SETTINGS_BRIDGE_TOKEN=<secret>
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
Дополнительные Keycloak-standard env version-specific (KC_DB, hostname/proxy/health/metrics/pool) фиксируются в .env.example после выбора image. Секреты только root .env/secret mounts с минимальными permissions.
Product limits otp.phone.* не дублируются env. OTP TTL/max verify attempts — security technical config provider-а; их имена нужно добавить в arch-04 до реализации, например:
KEYCLOAK_OTP_TTL_SEC=300
KEYCLOAK_OTP_MAX_VERIFY_ATTEMPTS=5
KEYCLOAK_OTP_SETTINGS_MAX_STALE_SEC=300
KEYCLOAK_OTP_HMAC_KEY=<secret>
19. Health, readiness и startup
Keycloak management health endpoints включены. Compose проверяет liveness/startup; readiness требует:
- server started;
- DB reachable/schema migration complete;
- realm/client/auth flow/provider loaded;
- active signing key;
- settings bridge last-known-good для OTP send;
- mock enabled с valid secret либо реальный provider configured.
Стандартный Keycloak health сам не знает business provider state; custom provider readiness check/sidecar/synthetic internal check дополняет его. Public synthetic проверяет discovery/JWKS и authorization endpoint без отправки OTP.
DB/settings failure не должен приводить к выдаче tokens без OTP. OTEL/metrics outage не блокирует login.
20. Logging, metrics, tracing и audit
Logs
JSON/stdout:
- service/version/environment, event/category;
- request/trace id, realm/client, safe flow step;
- result/error code, duration;
- phone только HMAC/masked при необходимости.
Запрещены raw OTP/mock code, phone, access/refresh/code, cookies, Authorization, client/admin secret, password, form body, redirect query с code, DB URL.
Keycloak access log должен редактировать sensitive query. TRACE/DEBUG production выключены.
Events/audit
Включаются login/login_error, logout, refresh/revoke, user create/disable, phone verify/change, brute-force/OTP limit, admin config changes. Retention/consumer определяется ops/legal; event payload минимален.
Metrics
- login/OTP send/verify success/failure/latency;
- limit/lockout rejects;
- settings cache age/refresh failures;
- active sessions/token refresh/error;
- DB pool/JVM/GC/HTTP;
- JWKS/key age;
- provider mode info (
mock, later vendor), без phone labels.
Tracing
OTEL support зависит от версии; HTTP/provider/settings bridge spans добавляются instrumentation без secrets. Если native tracing недостаточно, сохраняются request/trace correlation headers. Наблюдаемость не меняет auth outcome.
21. Backup, restore и disaster recovery
- managed PostgreSQL daily backup + PITR;
- realm config export хранится versioned и secret-free;
- signing key/private realm state входит в protected DB backup;
- provider JAR/theme/image reproducible из repo/artifacts;
- restore rehearsal в isolated environment;
- после restore проверяются issuer, keys/JWKS, clients/flows, user/session consistency, provider tables;
- RPO/RTO фиксируются ops до production.
При восстановлении в другой host нельзя случайно выдать production tokens с неверным issuer. DNS/TLS/hostname проверяются до открытия traffic. Backup encrypted/access-controlled; OTP expired rows очищаются по TTL.
22. Миграции и upgrades
Порядок:
- прочитать release notes и supported DB upgrade path;
- backup/PITR checkpoint;
- проверить provider SPI/API compatibility и пересобрать JAR;
- прогнать upgrade clone БД;
- contract/E2E login+refresh+logout;
- staged maintenance/rolling rollout только если версия поддерживает cluster compatibility;
- проверить schema migration, realm drift, JWKS;
- rollback приложения возможен только если DB schema backward-compatible; иначе restore/forward-fix runbook.
Нельзя пропускать major versions произвольно. Realm changes versioned отдельно. Custom provider migration имеет собственный version table/compatibility matrix.
23. Docker/runtime hardening
- service
keycloak,expose: 8080и management port только internal; - networks
public(только nginx access при необходимости),backend,observability; - без host
ports; - non-root, read-only rootfs где совместимо, tmpfs для temp;
- no-new-privileges/drop capabilities;
- memory/CPU/JVM heap limits, graceful termination;
- startup/readiness probes с достаточным initial period;
- immutable provider/theme mounts/image;
- no local persistent DB volume.
TLS client→nginx; Keycloak→managed PG TLS. Internal nginx→Keycloak HTTP допустим только на закрытой сети одной VM.
24. Failure semantics
| Сбой | Поведение |
|---|---|
| DB down | not-ready; login/refresh fail; existing access tokens проверяются API до exp по cached JWKS |
| settings bridge down, cache valid | send limits по last-known-good |
| settings bridge down, cache empty/stale | new OTP send fail-closed |
| mock secret missing/invalid | startup/not-ready; OTP не bypass |
| SMS mode без provider | not-ready otp_provider_unconfigured |
| wrong OTP | generic error, increment counter |
| too many sends/verifies | temporary reject/lockout, safe UX |
| token signing key rotation | old keys passive в JWKS grace |
| OTEL down | auth работает, telemetry drop metric/local log |
| provider exception | flow fail-closed, generic error/request id |
Не должно быть fallback на password или «успешный OTP» при инфраструктурной ошибке.
25. Тестовая матрица
Unit provider
- E.164 normalization across RU/international/Unicode;
- invalid/impossible phone;
- unique concurrent reservation;
- mock constant-time compare/redaction;
- challenge TTL/one-time/replay/concurrent verify;
- send/verify limits and window boundaries;
- settings cache/ETag/stale/fail-closed;
- phone HMAC/counter cleanup;
- provider SPI error mapping.
Realm/config contract
- only standard code+PKCE S256;
- password/direct/implicit/social disabled;
- exact origins/redirect/logout URIs;
- audience/claims/issuer;
- token/session TTL and refresh rotation;
- browser flow executions/required actions;
- no secrets/private keys in realm export.
Integration
- managed/test PostgreSQL schema/currentSchema/TLS;
- restart preserves counters/challenges;
- Keycloak upgrade/provider migration;
- settings bridge token/path with module-01;
- JWKS rotation and API validation;
- disabled user/revocation/not-before;
- brute-force lockout/recovery;
- proxy hostname/path builds correct external URLs.
E2E
- new phone → mock OTP → PKCE tokens → API bootstrap;
- existing user login; valid refresh without OTP;
- expired/revoked/rotated refresh → re-auth;
- wrong/expired/replayed code;
- max sends/min interval/max verifies;
- concurrent tabs/refresh single-flight assumptions;
- logout web/native;
- DB/settings outage;
- no phone/OTP/token in logs, URLs or metrics;
- admin endpoint inaccessible publicly.
Security
- redirect/open redirect, PKCE downgrade, state/nonce;
- user enumeration/timing;
- cookie flags/CSRF on auth forms;
- forwarded header spoofing;
- brute-force/IP/phone distributed attempts;
- JWT alg/aud/iss/kid attacks;
- secret scanning/image/SBOM/provider dependency review.
26. Definition of Done
- Keycloak доступен за
/auth, issuer/discovery/JWKS стабильны; - realm/client topology и PKCE S256 зафиксированы declaratively;
- только phone OTP; password/implicit/direct/social отключены;
- phone canonical E.164 и storage-level unique;
- claims соответствуют module-01 (
sub,phone_number, audience); - mock secret only env, не логируется/не отдаётся;
- OTP challenges/counters durable в Keycloak schema;
- product limits читаются только через canonical settings bridge/token;
- brute-force, TTL, verify attempts и enumeration protection работают;
- refresh rotation/reuse detection/logout/revocation покрыты;
- proxy/redirect/origin/CORS/cookies/TLS boundaries проверены;
- DB role/schema/backup/restore/upgrade runbooks готовы;
- health/metrics/logging/tracing не раскрывают secrets/PII;
- container hardening/root Compose без published port;
- test matrix зелёная;
- реальный SMS явно остаётся extension point, не скрытой заглушкой.
27. Решения, допущения и TBD
Решения:
- K1: realm
han-chat, public clienthan-chat-frontend, audiencehan-chat-api. - K2: Authorization Code + PKCE S256; остальные user grants выключены.
- K3: canonical identity/claim — E.164
phone_number;subimmutable. - K4: OTP authenticator/provider SPI; mock code только secret env.
- K5: counters/challenges в provider-owned PostgreSQL schema
keycloak, не API Redis. - K6: product limits только
/internal/settings/v1/otp+KEYCLOAK_SETTINGS_BRIDGE_TOKEN. - K7: refresh rotation/revoke-on-use; frontend single-flight.
- K8: real SMS provider — extension point/TBD.
Допущения:
- A1: единый public host
tohin.ruи relative path/auth. - A2: Keycloak version поддерживает нужные hostname/proxy/health options; точные names pin после выбора image.
- A3: product допускает mock OTP в первой production-like среде как временный риск.
- A4: телефон в access token необходим API bootstrap и защищён TLS/short token TTL.
TBD:
- K-TBD1: выбрать/pin Keycloak version и проверить custom SPI compatibility.
- K-TBD2: окончательные redirect URI для Expo iOS/Android и universal/app links.
- K-TBD3: финальные token/session TTL и brute-force thresholds после security review.
- K-TBD4: exact schema/migration mechanism provider tables без вмешательства в standard schema.
- K-TBD5: admin MFA/ops access topology и отдельный admin hostname.
- K-TBD6: signing-key rotation interval/HSM и emergency revocation.
- K-TBD7: RPO/RTO/event retention/legal deletion.
- K-TBD8: SMS vendor, credentials, templates, sender, delivery receipts and failover.
- K-TBD9: CAPTCHA/risk scoring после mock.
- K-TBD10: добавить proposed OTP technical env в arch-04 до реализации.