Files
fission-console/doc/descriptions/auth-flow.md
T

9.6 KiB
Raw Blame History

Логика авторизации — 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 <token>
    │   401 → ошибка | другой → ОК
    │   Кэш на 5 минут (sync.Map, ключ "env:token")
    │
    └─► identityFromJWT → decode payload (base64url, без верификации подписи)
        → claim "sub" → Sub
        → claim "email" → Email (опционально)

Deck API URL по стендам:

  • prodhttps://deck-api.ngcloud.ru/api/v1
  • devhttps://deck-api-dev.ngcloud.ru/api/v1
  • testhttps://deck-api-test.ngcloud.ru/api/v1

TestAuthenticator (тест-режим, устаревший)

Принимает JWT — декодирует sub из payload. Или строку с @ — использует как sub напрямую. Не ходит в Deck API. Включался через FISSION_TEST_MODE=true.

⚠️ Сейчас не используется — заменён на DemoAuthenticator.


Вычисление namespace: NamespaceForSub

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 инициализации

Бэкенд: handleAuthhandlers.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-..." }

Валидация токена: validateDeckTokenserver.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: namespaceFromJWTauth.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 / jwtEmailauth.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() — загрузить функции