09 Frontend Platform

Frontend Platform

Frontend Platform

Зачем нужен этот раздел

Этот раздел фиксирует роль frontend-ksailab в текущем auth/access контуре KSAILab после перехода на backend-owned browser session.

Проблема прошлых итераций была в том, что backend и IAM обсуждались отдельно, а реальная пользовательская поверхность доступа была размазана между меню, route guards, login pages и placeholder-экранами. Из-за этого команда легко скатывалась в спор, кто именно "владеет" browser auth flow.

Этот документ снимает двусмысленность:

  • canonical human/browser auth flow принадлежит backend;
  • frontend является consumer backend session/bootstrap;
  • frontend использует effective_capabilities для UI gating;
  • frontend не владеет canonical OIDC code exchange.

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

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

Что представляет собой текущий фронт

Текущий frontend-ksailab — это Nuxt 4 приложение с dashboard-навигацией и уже заметной платформенной поверхностью:

  • главная;
  • входящие;
  • образование;
  • мониторинг;
  • система;
  • разрешения;
  • модули;
  • настройки;
  • профиль.

Именно этот UI уже диктует, какие global capabilities нужны для меню, страниц, CTA и route guards.

Что есть во фронте сейчас

Навигация

Основная навигация описана в app/layouts/default.vue и уже задаёт реальный список разделов, которые нужно открывать или скрывать по access context.

Аутентификация

Текущий frontend auth-слой нужно читать как переходный, но уже частично aligned с backend-owned contract:

  • useAuth.ts хранит frontend auth-state;
  • authRepository.ts уже читает GET /api/v1/auth/me;
  • app/pages/auth/callback.vue уже работает как post-session bootstrap page;
  • infrastructure HTTP client уже использует cookie/session semantics;
  • menu и route visibility постепенно переводятся на capability-driven access context.

При этом переходное состояние ещё не завершено:

  • runtime defaults для login/logout уже выровнены на backend entrypoints, но logout UX и Keycloak SSO semantics ещё не доведены до финального full platform logout;
  • часть guard logic ещё нуждается в cleanup, чтобы public auth routes не bootstrap-ились раньше времени;
  • не весь UI ещё доведён до чистого capability-driven поведения.

Одновременно backend уже изменился:

  • canonical browser login entrypoint теперь GET /api/v1/auth/login;
  • canonical callback endpoint теперь GET /api/v1/auth/callback;
  • canonical logout endpoint теперь POST /api/v1/auth/logout;
  • canonical bootstrap endpoint теперь GET /api/v1/auth/me;
  • отдельного /capabilities endpoint в этой фазе нет;
  • local POST /api/v1/auth/login и POST /api/v1/auth/refresh возвращают 410 Gone;
  • onboarding orchestration уже существует в POST /api/v1/onboarding/*.

Из companion contracts для следующей wave frontend должен читать ещё и это:

  • POST /api/v1/auth/logout нельзя трактовать только как local cleanup action;
  • self-service security должна стартовать через GET /api/v1/auth/account/security, а не через legacy /users/password/* UX;
  • logged-out, session-expired и unauthorized должны оставаться разными UX-сценариями;
  • продуктовая кнопка Выйти должна приводить к full platform logout, а не к мгновенному silent re-login через живую Keycloak SSO session.

Экраны в состоянии partial contract / data-ready shell

Часть экранов уже полезна как реальная архитектурная поверхность, но backend раскрыт для них не полностью:

  • permissions/users уже работает на реальных onboarding и user-directory контрактах;
  • settings/security больше не является локальной password form и используется как backend-owned self-service shell для account security;
  • permissions/groups переведён на честный backend snapshot без локального CRUD, но bundle/group assignment contract ещё отсутствует;
  • permissions/roles переведён на list/detail/create-shell flow без мокового CRUD, но backend role mapping contract ещё отсутствует;
  • часть monitoring/system экранов всё ещё остаётся UI-shell.

Их всё равно нужно учитывать уже сейчас, потому что именно они формируют будущую административную модель.

Для первой полноценной реализации эти разделы должны получить такие роли:

  • permissions/users — teacher accounts, one-time credential handoff, assignments, overrides, user lifecycle;
  • settings/security — self-service password/recovery shell через backend account security entrypoint;
  • permissions/groups — bundles и групповые назначения;
  • permissions/roles — mapping realm roles к starter bundles.

Отдельно важно зафиксировать onboarding surface:

  • education/groups — создание групп и roster actions;
  • education/groups -> group detail — bulk onboarding студентов;
  • teacher-side создание пользователей из старого фронта не переносится как разрешённый flow v1.

Роль фронта в auth-схеме

Frontend не должен быть источником истины по доступам. Его задача — корректно потреблять уже рассчитанный access context.

Фронт должен

  • отправлять browser login в backend entrypoint, а не напрямую в Keycloak по умолчанию;
  • получать principal и capabilities через backend;
  • рендерить меню по capabilities;
  • применять route guards по capabilities;
  • применять component-level visibility helpers;
  • не дублировать backend ABAC на уровне бизнес-логики.

Фронт не должен

  • считать роль единственным источником доступа;
  • парсить JWT как основной permission source;
  • владеть canonical browser code exchange;
  • строить browser auth вокруг frontend-managed access/refresh lifecycle;
  • хранить ad-hoc набор булевых флагов под каждый экран;
  • считать hidden button достаточной защитой.

Canonical Browser Flow

Для frontend canonical human/browser auth contract выглядит так:

  1. Frontend отправляет browser на GET /api/v1/auth/login.
  2. Backend уводит browser в Keycloak.
  3. Keycloak возвращает browser на GET /api/v1/auth/callback.
  4. Backend выполняет callback/exchange, формирует server-side auth session и редиректит browser обратно на frontend route.
  5. Frontend запрашивает GET /api/v1/auth/me.
  6. Frontend получает:
    • principal;
    • classification;
    • realm_roles;
    • bundles;
    • effective_capabilities.
  7. Меню, страницы и компоненты рендерятся на основе capabilities.
  8. Любой реальный action всё равно повторно валидируется backend.
flowchart LR
    F[Frontend] -->|GET /api/v1/auth/login| B[Backend]
    B -->|Redirect| K[Keycloak]
    K -->|GET /api/v1/auth/callback| B
    B -->|Set opaque cookie + redirect| F
    F -->|GET /api/v1/auth/me| B

Для frontend эту диаграмму нужно читать так:

  • browser получает только session key cookie;
  • auth session и tokens не живут в frontend state как canonical source;
  • /auth/me остаётся единственным bootstrap surface для UI.

Logout semantics for frontend

Frontend должен различать два уровня logout, а не скрывать их за одним действием:

Local app logout

  • backend session и локальная app cookie больше невалидны;
  • frontend переходит в logged-out состояние;
  • Keycloak SSO session может ещё оставаться живой;
  • это объясняет, почему временный logged-out UX workaround на frontend ещё не должен приниматься за финальный продуктовый контракт.

Full platform logout

  • local app session завершена;
  • Keycloak SSO session тоже завершена;
  • следующий заход требует реального login flow, а не silent re-login;
  • именно это должно быть user-facing meaning кнопки Выйти в KSAILab.

Что сейчас остаётся переходным

  • frontend-ksailab на main всё ещё содержит logged-out recovery semantics, чтобы не отправлять пользователя обратно в мгновенный re-login;
  • это нужно описывать как transitional UX gap до завершения backend-ksailab#14 и ksailab-keycloak-scripts#9, а не как целевой смысл logout.

Transition vs target client model

Current transition model

Текущее переходное состояние нужно читать так:

  • canonical browser flow уже backend-owned;
  • frontend остаётся только consumer backend session/bootstrap;
  • существующий Keycloak client ksailab-frontend может ещё участвовать в runtime-совместимости;
  • это не должно документироваться как признак frontend ownership над browser auth flow.

Практическое следствие:

  • даже если backend временно инициирует browser login через текущую client config ksailab-frontend, canonical owner flow всё равно backend;
  • direct frontend-to-Keycloak login не должен описываться как поддерживаемый default browser contract.

Target model

Архитектурный фаворит:

  • отдельный backend browser-auth client в Keycloak;
  • явная redirect/logout policy под backend callback surface;
  • отсутствие двусмысленности между browser-session auth и explicit bearer/manual contract;
  • ksailab-frontend перестаёт трактоваться как долгосрочный canonical browser-login client.

Что именно фронт диктует архитектуре прав

Текущий фронт уже требует от backend и auth-системы как минимум следующих свойств:

  • стабильный capability catalog;
  • единый bootstrap endpoint;
  • чёткое разделение между global capabilities и resource-scoped ABAC;
  • возможность быстро подключать новый экран без полной переработки ролей;
  • понятную административную модель для раздела Разрешения.

Это означает, что frontend должен получать не только roles, но и уже рассчитанный набор:

  • effective_capabilities;
  • bundles;
  • principal и профильный контекст.

Отдельно важно: ksailab-frontend как client name не должен использоваться как аргумент в пользу direct frontend-to-Keycloak login по умолчанию.

Что backend уже подготовил для фронта

Для первой auth/onboarding волны backend уже landed:

  • GET /api/v1/auth/login;
  • GET /api/v1/auth/callback;
  • POST /api/v1/auth/logout;
  • GET /api/v1/auth/me;
  • отсутствие отдельного /capabilities endpoint в первой фазе;
  • deprecated 410 Gone для legacy local login/refresh;
  • 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.

Текущий runtime capability baseline:

  • auth.bootstrap.read
  • onboarding.teacher.create
  • onboarding.group.create
  • onboarding.group.assign_teacher
  • onboarding.student.bulk_create
  • onboarding.credentials.consume

Важно:

  • create-операции больше не должны проектироваться как ответы с обычным inline temporary_password;
  • frontend должен делать one-time receipt handoff;
  • explicit bearer flow допустим только как отдельный non-browser/manual сценарий;
  • bearer flow не должен становиться базой для browser bootstrap и не отменяет session/cookie contract;
  • browser cookie должна трактоваться как opaque session key, а не как место хранения token payload.

Как старый фронт помогает текущему этапу

Старый фронт прошлого проекта дал полезные UX-референсы для текущего этапа, хотя его auth-модель уже legacy:

  • ручное создание teacher account;
  • bulk creation groups;
  • bulk creation students с назначением в группу;
  • reset password и lifecycle actions как legacy UX reference only: в текущем продукте парольный self-service вынесен в backend-owned security flow.

Именно эти UX-сценарии нужно использовать как основу нового onboarding, но с другой политикой доступа:

  • provisioning выполняет только admin;
  • credentials управляются через Keycloak;
  • временный пароль раскрывается через receipt consume flow;
  • первый вход требует смены временного пароля.

Validation и expected tests

Фронтовая реализация будет считаться корректной, когда появятся:

  • tests на старт login через backend /api/v1/auth/login;
  • tests на bootstrap через GET /api/v1/auth/me;
  • tests на различие logged-out vs session-expired и на то, что logout не превращается обратно в implicit login success;
  • smoke на target full platform logout после завершения companion backend/keycloak changes;
  • tests, подтверждающие, что frontend не ожидает отдельный /capabilities endpoint;
  • tests на capability-driven menu и route guards;
  • tests на отсутствие premature bootstrap на public auth routes;
  • tests на onboarding teacher/group/student UI flows;
  • tests на одноразовый показ временного пароля через receipt consume flow.

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