02 Backend Platform

Backend Platform

Backend Platform

Статус и контекст

Этот раздел описывает реальное состояние backend-ksailab после auth foundation MR !10, follow-up reconciliation !13 и runtime browser-session sync в !15, а не только целевую архитектуру.

Важно:

  • текущий backend-репозиторий (backend-ksailab) был перенесён из предыдущего образовательного проекта;
  • доменная и CRUD-часть по пользователям, группам, темам, разделам, тестам и файлам уже была зрелой до миграции auth;
  • по состоянию на 2026-03-16 в backend уже landed Keycloak-first auth foundation, follow-up reconciliation по human identity readiness и runtime browser-session contract;
  • остаточный auth-foundation reconciliation дальше продолжается в ksailab/backend-ksailab#1, поэтому migration нельзя считать полностью законченной.

Связанные репозитории:

  • backend-ksailab
  • frontend-ksailab
  • keycloak-scripts-ksailab
  • docs-ksailab

Что backend представляет собой сейчас

По текущему коду backend уже реализует:

  • REST API на FastAPI;
  • хранение данных в PostgreSQL через SQLAlchemy;
  • работу с группами, темами, секциями, подразделами, тестами, вопросами и файлами;
  • часть бизнес-ограничений на уровне сервисов и security dependencies;
  • точечные ABAC-проверки для доступа к темам и учебному контенту;
  • Keycloak-first principal bootstrap через GET /api/v1/auth/me;
  • onboarding orchestration для teacher, group, teacher assignment и bulk student provisioning;
  • receipt-based handoff для одноразовой выдачи временного пароля;
  • startup и health diagnostics по Keycloak issuer и JWKS.

Одновременно backend всё ещё содержит legacy-следы старой системы:

  • старые local-auth endpoints ещё существуют как deprecated compatibility layer;
  • часть старого кода по-прежнему завязана на локальный профиль пользователя и числовой id;
  • часть reconciliation вокруг human admin readiness и legacy cleanup ещё остаётся в ksailab/backend-ksailab#1.

Что landed в auth foundation

В рамках backend MR !10, !13 и !15 в runtime уже зафиксировано следующее:

  • Keycloak стал источником identity и coarse-grained realm roles admin, teacher, student;
  • backend валидирует Keycloak token через issuer metadata и JWKS;
  • canonical browser login entry теперь GET /api/v1/auth/login;
  • canonical callback endpoint теперь GET /api/v1/auth/callback;
  • canonical logout endpoint теперь POST /api/v1/auth/logout;
  • frontend bootstrap теперь должен опираться на GET /api/v1/auth/me, который поднимает current principal из backend-owned session/cookie;
  • отдельного /capabilities endpoint в этой фазе нет;
  • local POST /api/v1/auth/login и POST /api/v1/auth/refresh больше не являются рабочим auth flow и возвращают 410 Gone;
  • explicit bearer path оставлен только как отдельно очерченный non-browser/manual Keycloak JWT flow;
  • onboarding вынесен в отдельный набор POST /api/v1/onboarding/* endpoints;
  • временный пароль больше не отдаётся inline из create-операций, а передаётся через receipt-based handoff;
  • /api/v1/health и startup logs показывают состояние связности с Keycloak и диагностическую human identity readiness.

Отдельно для следующей auth wave backend contract уже зафиксирован в ksailab/backend-ksailab#14:

  • browser cookie должен содержать только opaque session identifier;
  • browser auth session должна жить server-side;
  • target storage для неё — Redis;
  • canonical logout surface должен эволюционировать от local session cleanup к full platform logout.

Реальная роль backend в KSAILab

После миграции backend должен оставаться не IAM-системой, а оркестратором бизнес-логики и авторизации на платформенных данных.

Backend отвечает за

  • валидацию identity-контекста, полученного через Keycloak;
  • загрузку платформенного профиля и связей пользователя;
  • вычисление глобальных platform capabilities для UI;
  • исполнение resource-scoped ABAC по данным собственной БД;
  • выдачу GET /api/v1/auth/me как единого bootstrap-контракта для фронта;
  • вызовы Keycloak Admin API для user lifecycle из platform UI;
  • сохранение и проверку платформенных состояний, которых нет в IAM:
    • membership;
    • ownership;
    • co-author access;
    • archive state;
    • progress state;
    • publication and availability state.

Backend не должен больше быть источником истины для

  • primary credentials;
  • локального login/password flow как основной auth-модели;
  • собственного access/refresh token lifecycle как canonical auth source;
  • полного permission catalog внутри Keycloak.

Текущая backend auth-модель

1. Identity integration layer

Новый auth-слой backend уже включает:

  • OIDC/JWKS validation against Keycloak;
  • principal resolver;
  • profile binding;
  • normalised request context;
  • health/startup probe для issuer metadata и JWKS.

Минимальный runtime principal в backend:

  • id — локальный платформенный профиль;
  • username;
  • full_name;
  • role — классификация профиля;
  • keycloak_sub — внешний sub из Keycloak;
  • identity_source;
  • is_active;
  • is_archived.

2. Capability layer

Backend стал источником capability bootstrap для фронта.

В первой auth-foundation фазе bootstrap идёт только через GET /api/v1/auth/me. Отдельного /capabilities endpoint в этой фазе нет.

Сейчас effective access рассчитывается по модели:

principal + bundles + overrides

При этом важно различать четыре слоя:

  1. capability definitions — версионируемый каталог прав;
  2. bundles — рабочие наборы capabilities;
  3. assignments/overrides — административные назначения;
  4. ABAC — проверки по конкретным ресурсам.

Definitions фиксируются в коде и документации, а bundles и assignments должны жить в БД платформы и администрироваться через UI.

3. Policy layer

Любой защищённый endpoint в целевой модели должен раскладываться на три шага:

  1. identity validated;
  2. capability granted;
  3. resource-specific ABAC passed.

Это особенно важно для перехода от coarse-grained bootstrap к будущему полномасштабному ABAC Rollout.

Контракт между backend и frontend

GET /api/v1/auth/me

Текущий runtime bootstrap-контракт возвращает:

  • principal;
  • classification;
  • realm_roles;
  • bundles;
  • effective_capabilities.

Этого уже достаточно для:

  • initial render;
  • route guards;
  • menu gating;
  • component gating;
  • понимания, какие onboarding/action surfaces показывать пользователю.

Canonical browser-session surface

Backend runtime для human/browser auth теперь фиксирует такой surface:

  • GET /api/v1/auth/login — start browser login flow;
  • GET /api/v1/auth/callback — принять authorization code и поднять backend session;
  • POST /api/v1/auth/logout — canonical browser logout surface; current baseline очищает локальную backend session, target next wave должна довести его до full platform logout;
  • GET /api/v1/auth/me — вернуть bootstrap payload для уже поднятой session.

Storage semantics для этого surface нужно фиксировать явно:

  • browser cookie не должен быть местом хранения auth payload;
  • GET /api/v1/auth/me должен поднимать principal из server-side auth session;
  • access/refresh/id tokens должны жить server-side;
  • Redis является target store для этой auth session в следующей wave.

Этот контракт не должен описываться как frontend-managed bearer lifecycle.

Explicit bearer path

Backend всё ещё может принять Keycloak-issued bearer token, но только как явно отдельный non-browser/manual сценарий.

  • bearer token должен соответствовать настроенному Keycloak issuer;
  • bearer token не должен считаться canonical browser path;
  • browser flow должен документироваться через session/cookie contract.

Это важно и для logout semantics:

  • explicit bearer/manual flow не определяет browser logout поведение;
  • local app logout и full platform logout нужно описывать отдельно;
  • кнопка Выйти в продукте не должна закреплять за собой только local cleanup semantics.

Deprecated local-auth endpoints

Следующие endpoints больше не считаются рабочим auth flow:

  • POST /api/v1/auth/login
  • POST /api/v1/auth/refresh

Они оставлены только как deprecated compatibility layer и возвращают 410 Gone.

Provisioning orchestration layer

Backend уже поддерживает отдельный onboarding-контур поверх Keycloak Admin API.

Текущие canonical endpoints:

  • POST /api/v1/onboarding/teachers
  • POST /api/v1/onboarding/groups
  • POST /api/v1/onboarding/groups/{group_id}/teachers
  • POST /api/v1/onboarding/groups/{group_id}/students/bulk
  • POST /api/v1/onboarding/credentials/consume

Что важно по credential flow

  • identity создаётся в Keycloak;
  • create-операция возвращает credential_receipt, а не обычный temporary_password inline;
  • временный пароль раскрывается только через отдельный consume flow;
  • receipt одноразовый и ограничен по времени жизни;
  • первый вход по-прежнему требует смены пароля.

Что всё ещё считается переходным слоем

В backend остаются legacy endpoints старой системы, но для документации KSAILab они больше не являются основным auth/provisioning contract.

Они могут использоваться как transitional transport при миграции UI, но canonical поверхностью надо считать именно:

  • GET /api/v1/auth/me
  • POST /api/v1/onboarding/*

Source-of-truth boundaries

Для traceability backend docs должны явно разделять runtime contract и окружающую delivery-инфраструктуру:

  • shared CI source-of-truth для KsaiLab живёт в ci-pipelines/pipelines-ksailab;
  • backend deployment, ingress, callback-route exposure и runtime env source-of-truth живут в infra/infra-backend-ksailab;
  • Keycloak realm/bootstrap/provisioning source-of-truth живёт в keycloak-scripts-ksailab;
  • physical Keycloak deployment source-of-truth живёт в infra-keycloak-ksailab;
  • backend-ksailab!15 не заявляет скрытый infra diff: если rollout session/cookie semantics требует follow-up, он должен отслеживаться отдельно в infra-репозитории.

Граница ответственности остаётся такой:

  • backend runtime не создаёт первого human admin сам;
  • первый human admin появляется отдельным operator flow на стороне Keycloak;
  • ksailab-admin-service остаётся service account для provisioning, а не surrogate для human admin.

Health и операционная диагностика

/api/v1/health теперь должен отражать не только доступность backend, но и:

  • состояние связности с настроенным Keycloak issuer;
  • диагностическую human identity readiness по ролям admin, teacher, student.

Startup logs дополнительно показывают:

  • configured issuer;
  • JWKS URI;
  • количество доступных ключей;
  • ошибку связности, если issuer metadata не читается;
  • наличие или отсутствие human identities по coarse roles.

Важно: это диагностический probe. Он не должен автоматически создавать human admin и не делает local backend bootstrap актуальной auth-моделью.

Это важно для эксплуатационной диагностики при запуске стенда и при расследовании auth-инцидентов.

Что ещё остаётся незавершённым

Нельзя считать, что backend auth-миграция полностью завершена.

Отдельно остаются:

  • cleanup legacy local admin bootstrap;
  • синхронизация первого human admin с чистой Keycloak-first моделью;
  • дальнейшее развитие bundle/override persistence;
  • расширение resource-scoped ABAC поверх текущего bootstrap.

Эти остатки дальше отслеживаются в ksailab/backend-ksailab#1.

Validation и expected tests

Для этой волны архитектура уже опирается на реальные backend-проверки:

  • auth integration tests на GET /api/v1/auth/me;
  • tests на deprecated 410 Gone для local login/refresh;
  • onboarding tests на teacher/group/student flows;
  • tests на receipt consume и одноразовую выдачу временного пароля;
  • startup/health diagnostics tests для Keycloak issuer и JWKS.

Frontend migration tests для потребления этого контракта остаются отдельной задачей во frontend-ksailab.

Связанные документы