# Логика авторизации — Fission Console > ⚠️ ЛЕГАСИ (актуально до ~апреля 2026): описание ниже относится к старой схеме с Deck API + JWT. > Актуальное описание — **выше этого блока**, в разделе "АКТУАЛЬНО (май 2026+)". --- # АКТУАЛЬНО (май 2026+) ## Что принимает сервер Токен передаётся одним из двух способов: - Заголовок `X-Auth-Token: <токен>` - Заголовок `Authorization: Bearer <токен>` `X-Auth-Env` — необязателен, по умолчанию `test`. --- ## Типы токенов и маршрутизация (MultiAuthenticator) Сервер использует `MultiAuthenticator`, который определяет тип по форме токена: | Форма токена | Authenticator | Описание | |---|---|---| | JWT (три части через `.`) | `DeckAuthenticator` или `TestAuthenticator` | Реальный облачный токен или тест-JWT | | Любая строка ≥6 символов | `DemoAuthenticator` | Demo-логин без внешних запросов | | Строка <6 символов | — | Ошибка 401 | --- ## DemoAuthenticator (текущий основной режим) Принимает **любую строку ≥6 символов** как логин. Не ходит во внешние сервисы. ``` login (≥6 символов) │ ▼ Sub = UUID v5(login) ← детерминированный, фиксированный UUID namespace Email = "demo-lo***in" ← маскированный для отображения в UI │ ▼ Namespace = "fission-" + hex(SHA256(Sub)[:8]) ``` Пример: - login = `livetest@test.local` - Sub = `uuidV5("livetest@test.local")` = фиксированный UUID - Namespace = `fission-c3fce59430e41b0f` Каждый логин → один и тот же namespace (детерминированно). --- ## DeckAuthenticator (production JWT) Для JWT-токенов из облака NUBES: ``` JWT токен │ ├─► validateToken → GET {deckAPI}/index.cfm/instances │ Authorization: Bearer │ 401 → ошибка | другой → ОК │ Кэш на 5 минут (sync.Map, ключ "env:token") │ └─► identityFromJWT → decode payload (base64url, без верификации подписи) → claim "sub" → Sub → claim "email" → Email (опционально) ``` Deck API URL по стендам: - `prod` → `https://deck-api.ngcloud.ru/api/v1` - `dev` → `https://deck-api-dev.ngcloud.ru/api/v1` - `test` → `https://deck-api-test.ngcloud.ru/api/v1` --- ## TestAuthenticator (тест-режим, устаревший) Принимает JWT — декодирует sub из payload. Или строку с `@` — использует как sub напрямую. Не ходит в Deck API. Включался через `FISSION_TEST_MODE=true`. > ⚠️ Сейчас не используется — заменён на DemoAuthenticator. --- ## Вычисление namespace: `NamespaceForSub` ```go func NamespaceForSub(sub string) string { h := sha256.Sum256([]byte(sub)) return "fission-" + hex.EncodeToString(h[:8]) // 16 hex символов } ``` Namespace **детерминирован**: одинаковый логин → всегда один namespace. --- ## authMiddleware — что происходит на каждый запрос ``` Входящий HTTP запрос │ ├─► authTokenFromRequest → X-Auth-Token || Authorization: Bearer │ ├─► authenticator.Authenticate(token, env) → UserIdentity{Sub, Email} │ ошибка → 401 │ ├─► NamespaceForSub(Sub) → "fission-XXXXXXXXXXXXXXXX" │ ├─► nsManager.EnsureUserNS(namespace) │ → создаёт K8s namespace + RBAC если нет │ ошибка → 502 │ └─► контекст запроса: ctxKeyNS=namespace, ctxKeyIdentity=identity → handler работает в namespace пользователя ``` --- ## POST /auth — явный вход ``` POST /console/api/auth Body: { "token": "...", "env": "test" } │ └─► то же что authMiddleware, возвращает: { ok: true, namespace: "fission-...", email: "demo-lo***in" } ``` --- ## Итого — полная схема (актуальная) ``` Пользователь передаёт токен/логин │ ▼ MultiAuthenticator ├── JWT? → DeckAuthenticator (Deck API + JWT decode) └── нет → DemoAuthenticator (≥6 символов → UUID v5) │ ▼ UserIdentity { Sub, Email } │ ▼ NamespaceForSub(Sub) = "fission-" + hex(SHA256(Sub)[:8]) │ ▼ EnsureUserNS → K8s namespace существует │ ▼ Все хендлеры работают с namespace из context ``` --- # ЛЕГАСИ (до апреля 2026) — не удалять, для анализа ## Что вводит пользователь В форме логина (`index.html`, overlay `#login-overlay`): - **Стенд** (`#l-env`): `dev` / `test` / `prod` - **Токен** (`#l-token`): JWT-токен из личного кабинета NUBES (Профиль → Токены) --- ## Фронтенд: `doLogin()` → `js/auth.js` 1. Берёт токен и стенд из формы 2. Делает `POST /console/api/auth` с `{ token, env }` в теле 3. Если ответ OK: - Сохраняет `auth_token` и `auth_env` в **localStorage** браузера - Вызывает `setUserAvatar(token)` — декодирует email из JWT payload и показывает в navbar - Скрывает overlay логина 4. Проверяет `GET /console/api/ns/status` — если namespace не готов, показывает overlay инициализации --- ## Бэкенд: `handleAuth` → `handlers.go:1022` 1. Принимает `POST /console/api/auth` 2. Валидирует токен через `resolveNamespaceForToken(token, env, testMode)` 3. Вызывает `EnsureUserNS` — гарантирует что K8s namespace + RBAC + quota существуют 4. Возвращает JSON: `{ ok: true, env: "test", namespace: "fission-..." }` --- ## Валидация токена: `validateDeckToken` → `server.go:279` **Deck API** — внешний сервис облачной платформы NUBES: ``` dev → https://deck-api-dev.ngcloud.ru/api/v1 test → https://deck-api-test.ngcloud.ru/api/v1 prod → https://deck-api.ngcloud.ru/api/v1 ``` Проверка: `GET {deckAPI}/index.cfm/instances` с заголовком `Authorization: Bearer {token}` - `401` → токен невалиден - Любой другой ответ → токен принят - Результат кэшируется на **5 минут** (`sync.Map`) --- ## Вычисление namespace: `namespaceFromJWT` → `auth.go:131` 1. JWT токен **не верифицируется по подписи** — Deck API уже это сделал 2. Декодируется payload (base64url) 3. Извлекается claim `"sub"` (идентификатор пользователя в облаке) 4. Namespace = `"fission-" + hex(SHA256(sub)[:8])` Пример: sub = `c3fce59430e41b0f...` → namespace `fission-c3fce59430e41b0f` --- ## Email в navbar: `setUserAvatar` / `jwtEmail` → `auth.js` - `jwtEmail(token)` — декодирует JWT payload на **фронтенде** (без обращения к серверу) - Берёт `payload.email` или `payload.sub` - Отображает в `#user-avatar` в левом верхнем углу (вместо синего кружка) --- ## Последующие запросы Каждый API-запрос от фронтенда включает заголовки: ``` X-Auth-Token: <токен из localStorage> X-Auth-Env: <стенд из localStorage> ``` `authMiddleware` (`auth.go`) снова валидирует токен (но из кэша — без нового HTTP-запроса к Deck API), вычисляет namespace и кладёт его в `context.Context` → все хендлеры работают в нужном namespace. --- ## Test-режим (FISSION_TEST_MODE=true) Вместо реального Deck API — проверяет заголовок `X-Test-Sub`. Если токен содержит `@` — трактуется как email и используется как sub для вычисления namespace. Используется в тестах и для live-тестирования без реальных токенов. --- ## Итого — схема ``` Пользователь вводит токен + стенд │ ▼ POST /console/api/auth │ ├─► validateDeckToken → Deck API ({env}.ngcloud.ru) → 200 = ОК │ ├─► namespaceFromJWT → decode JWT payload → claim "sub" → SHA256[:8] → "fission-XXXXXXXX" │ ├─► EnsureUserNS → K8s namespace + RBAC создаются если нет │ └─► { ok: true, namespace: "fission-..." } Фронтенд: ├─► localStorage.setItem('auth_token', token) ├─► localStorage.setItem('auth_env', env) ├─► jwtEmail(token) → email из JWT payload → показать в navbar └─► reloadAll() — загрузить функции ```