# 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` | `deck-test.ngcloud.ru` |

Источник: HAR-файл `Files/nubes_login.har` (в query-параметрах auth request).

---

## 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 (альтернативный путь)

Из HAR: код обменивается не напрямую в Keycloak, а через **API Gateway ngcloud**:

```
POST https://lk-api-gateway-test.ngcloud.ru/api/v1/iam/auth/token
Content-Type: application/json
Body: {"code": "<authorization_code>"}
```

Ответ:
```json
{
  "access_token": "...",
  "refresh_token": "...",
  "expires_in": 43200
}
```

---

## 5. JWT-токены

### 5.1. Токен из `Files/token.txt` (test-окружение)

| Поле | Значение |
|---|---|
| `iss` | `https://auth.k8s.ngcloud.ru` |
| `aud` | `backend` |
| `sub` | `tazetdinovn@gmail.com` |
| `name` | Наиль Тазетдинов |
| `email` | `tazetdinovn@gmail.com` |
| `expires_in` | 86400 (24ч) |
| `scope` | `openid` |
| `token_type` | `Bearer` |

Claims (массив):
- `""`
- `""`
- `wz03709-shturval-admin-iot-naeel` — namespace в k8s (Fission)
- `wz03709-shturval-admin-naeel-test-3` — ещё один namespace

> ⚠️ `ClientID` в этом токене **отсутствует** — это другой issuer (`auth.k8s.ngcloud.ru`), не Keycloak.

### 5.2. Токен из HAR (deck-test)

| Поле | Значение |
|---|---|
| `iss` | `auth-api` |
| `ClientID` | `WZ01325` |
| `company_id` | `3e64aac6-dcfc-4082-88dc-da19c86555a5` |
| `company_name` | Тест |
| `login` | `tazet@narod.ru` |
| `firstname` | Наиль |
| `lastname` | Тазетдинов |
| `email` | `tazet@narod.ru` |

### 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: 'tazet@narod.ru' },
  { 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-пользователи |
