185 lines
6.4 KiB
Markdown
185 lines
6.4 KiB
Markdown
# Keycloak Auth Reference — консолидированная справка
|
||
|
||
> Обновлено: 2026-06-02
|
||
> Файл-ориентир. Править по мере уточнения у DevOps и продвижения проекта.
|
||
|
||
---
|
||
|
||
## 1. Keycloak-эндпоинты
|
||
|
||
Realm: **`cloud`** на `keycloak.nubes.ru`
|
||
|
||
| Назначение | URL |
|
||
|---|---|
|
||
| Authorization | `https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/auth` |
|
||
| Token exchange | `https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/token` |
|
||
| JWKS (сертификаты) | `https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/certs` |
|
||
| Logout | `https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/logout` |
|
||
|
||
---
|
||
|
||
## 2. Зарегистрированное приложение (OAuth client)
|
||
|
||
`client_id` — выдаётся DevOps при регистрации приложения в Keycloak.
|
||
|
||
---
|
||
|
||
## 3. Схема аутентификации: Authorization Code Flow
|
||
|
||
```
|
||
1. Браузер → /login (приложение)
|
||
2. Приложение → 302 → Keycloak /auth?client_id=...&scope=openid&redirect_uri=...&state=...
|
||
3. Пользователь вводит логин/пароль в Keycloak
|
||
4. Пользователь вводит OTP (MFA)
|
||
5. Keycloak → 302 → /callback?code=...&state=...
|
||
6. Приложение → POST /token (code → access_token + refresh_token + id_token)
|
||
7. Валидация JWT через JWKS (RS256)
|
||
8. Извлечение claims → req.user → сессия
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Получение токена через API Gateway (альтернативный путь)
|
||
|
||
Если в инфраструктуре есть API Gateway, код обменивается не напрямую в Keycloak, а через него:
|
||
|
||
```
|
||
POST <url API Gateway>/token
|
||
Content-Type: application/json
|
||
Body: {"code": "<authorization_code>"}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. JWT-токены
|
||
|
||
### 5.1. Пример структуры JWT (deprecated)
|
||
|
||
```
|
||
iss: https://<issuer>/...
|
||
aud: <audience>
|
||
sub: <email>
|
||
name: <ФИО пользователя>
|
||
email: <email пользователя>
|
||
exp: <timestamp>
|
||
scope: openid
|
||
token_type: Bearer
|
||
```
|
||
|
||
> ⚠️ `ClientID` может отсутствовать — зависит от issuer'а.
|
||
|
||
### 5.2. Пример токена
|
||
|
||
| Поле | Значение |
|
||
|---|---|
|
||
| `iss` | `auth-api` |
|
||
| `ClientID` | `WZ01325` |
|
||
| `company_id` | `<uuid>` |
|
||
| `company_name` | Название компании |
|
||
| `login` | `user@example.com` |
|
||
| `email` | `user@example.com` |
|
||
|
||
### 5.3. Целевой формат токена (из ТЗ)
|
||
|
||
| Claim | Назначение |
|
||
|---|---|
|
||
| `ClientID` | Идентификатор компании (WZ-номер) |
|
||
| `company_id` | UUID компании |
|
||
| `company_name` | Название компании |
|
||
| `email` | Email пользователя (для аудита) |
|
||
| `groups` | ? (пока null) |
|
||
| `roles` | ? (пока null) |
|
||
|
||
---
|
||
|
||
## 6. Роли
|
||
|
||
| Роль | ClientID | Права |
|
||
|---|---|---|
|
||
| **Администратор** | `WZ01112` | Все записи, просмотр аудита, экспорт |
|
||
| **Клиент** | любой другой WZ-номер | Только свои записи |
|
||
|
||
> Как именно ClientID попадает в токен — через группы Keycloak или маппер — **требует уточнения у DevOps**.
|
||
|
||
---
|
||
|
||
## 7. Реализация в коде
|
||
|
||
### `src/auth.js` — два режима
|
||
|
||
| Режим | Условие | Как работает |
|
||
|---|---|---|
|
||
| **OIDC** | `KC_CLIENT_ID` + `KC_CLIENT_SECRET` заданы | Authorization Code Flow, JWKS-валидация |
|
||
| **Mock** | `KC_CLIENT_ID` пусто | Локальная RSA-пара, JWT из `MOCK_USERS` |
|
||
|
||
### `src/config.js` — Mock-пользователи
|
||
|
||
```javascript
|
||
MOCK_USERS = [
|
||
{ clientId: 'WZ01325', companyName: 'Тест', email: 'test@example.com' },
|
||
{ clientId: 'WZ01112', companyName: 'Нубес', email: 'admin@nubes.ru' }
|
||
]
|
||
```
|
||
|
||
### Два слоя аутентификации
|
||
|
||
| Слой | Механизм | Где |
|
||
|---|---|---|
|
||
| **UI (EJS)** | Session cookie + PostgreSQL session store | `/`, `/admin`, `/entries` |
|
||
| **REST API** | Bearer JWT (всегда проверяется явно) | `/api/v1/*` |
|
||
|
||
### Dev-backdoor
|
||
|
||
- `DEV_MODE=true` или `DEV_SECRET` → `/dev-login` — выбор любого пользователя в обход OIDC/mock
|
||
|
||
---
|
||
|
||
## 8. Переменные окружения
|
||
|
||
```env
|
||
# Auth
|
||
DEV_MODE=true
|
||
JWT_ISSUER=mock-auth-api
|
||
ADMIN_CLIENT_ID=WZ01112
|
||
|
||
# OIDC (production)
|
||
KC_BASE_URL=https://keycloak.nubes.ru/realms/cloud
|
||
KC_CLIENT_ID=
|
||
KC_CLIENT_SECRET=
|
||
APP_URL=http://localhost:3000
|
||
|
||
# Session
|
||
SESSION_SECRET=<обязателен в prod>
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Открытые вопросы (к DevOps)
|
||
|
||
| # | Вопрос | Статус |
|
||
|---|---|---|
|
||
| 1 | Где брать `client_id` и `client_secret` для регистрации приложения в Keycloak? | 🔴 |
|
||
| 2 | Какой URL JWKS? `auth-api` или `keycloak.nubes.ru`? | 🔴 |
|
||
| 3 | Когда заполняются `claims.groups` и `claims.roles`? (пока null) | 🔴 |
|
||
| 4 | Как выглядит токен, если у пользователя несколько компаний? Массив `ClientID`? | 🔴 |
|
||
| 5 | Есть ли в Keycloak понятие admin-роли? В каком claim? | 🔴 |
|
||
| 6 | Redirect URI: `https://white.nodejsk8s.dev.nubes.ru/callback` — OK? | 🔴 |
|
||
| 7 | Кто потребитель `/export`? Какие IP/подсети для allowlist? | 🔴 |
|
||
| 8 | Формат `jsonEnv` — пример правильного? | 🔴 |
|
||
|
||
---
|
||
|
||
## 10. Исходные файлы
|
||
|
||
| Файл | Содержание |
|
||
|---|---|
|
||
| `docs/ТЗ.md` | Техническое задание |
|
||
| `docs/ТЗ-реализация.md` | Карта реализации требований |
|
||
| `docs/questions-devops.md` | Вопросы к DevOps |
|
||
| `research/auth-flow.md` | Детальное исследование OIDC-flow |
|
||
| `research/payg-sso/` | CF-эталон (OAuth2 + JWT RS256) |
|
||
| `Files/token.txt` | Реальный токен из test-окружения |
|
||
| `Files/nubes_login.har` | HAR трассировка login-сессии deck-test |
|
||
| `src/auth.js` | Основной модуль аутентификации |
|
||
| `src/config.js` | Конфигурация и mock-пользователи |
|