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-ksailabbackend-ksailabdocs-ksailabkeycloak-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; - отдельного
/capabilitiesendpoint в этой фазе нет; - local
POST /api/v1/auth/loginиPOST /api/v1/auth/refreshвозвращают410 Gone; - onboarding orchestration уже существует в
POST /api/v1/onboarding/*. - backend MR
!30дополнительно закрепил, что учебные группы, access groups иpermissions/groupsявляются разными surfaces, а content/question/file actions должны проходить backend resource-scope checks.
Из 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 контрактах и держит current lifecycle surface как disable/offboarding, а не как обычный delete;settings/securityбольше не является локальной password form и используется как backend-owned self-service shell для account security;permissions/groupsпереведён на честный backend snapshot/read-only surface без локального CRUD, а отдельный access-group runtime уже вынесен в/api/v1/access-groups;permissions/rolesпереведён на list/detail/create-shell flow без мокового CRUD: system roles читаются как immutable runtime baseline, а custom-role write model ещё не опубликована;- organization-related forms больше не должны жить в локальных догадках: backend уже публикует tenant mode как runtime contract через
contractsblock; - часть monitoring/system экранов всё ещё остаётся UI-shell.
Их всё равно нужно учитывать уже сейчас, потому что именно они формируют будущую административную модель.
Для первой полноценной реализации эти разделы должны получить такие роли:
permissions/users— teacher accounts, one-time credential handoff, assignments, overrides и safe lifecycle через disable/offboarding;settings/security— self-service password/recovery shell через backend account security entrypoint;permissions/groups— read-only metadata surface; отдельный runtime/admin слой для access groups уже landed в backend под/api/v1/access-groups, а dedicated frontend write UI остаётся follow-up;permissions/roles— audit immutable system role mappings и отдельный future custom-role layer, а не editor дляadmin/teacher/student.
Отдельно важно зафиксировать onboarding surface:
education/groups— создание групп и roster actions;education/groups -> group detail— bulk onboarding студентов;- teacher-side создание пользователей из старого фронта не переносится как разрешённый flow v1.
Runtime boundaries for roles, groups, lifecycle, and tenant policy
Чтобы frontend не возвращался к fake CRUD и локальным догадкам, текущую wave нужно читать так:
admin,teacher,studentостаются immutable system roles;/permissions/roles/new— это data-ready shell для будущей custom-role model, а не редактор системных ролей;permissions/groupsостаётся snapshot/read-only surface permission-side данных; реальный group lifecycle живёт вeducation/groups, а access-group lifecycle уже вынесен в/api/v1/access-groups;education/groupsработает с учебными группами через/api/v1/groups/management*, а не с access assignment;education/coursesдо отдельного backend contract должен оставаться topic-based shell: course-like ancestry идёт черезtopics -> sections -> subsections -> tests;education/question-bankи test creation должны учитывать, что archived/wrong-scope questions нельзя использовать, а student attempt payload не раскрывает correct answers;- file/library UI должен обновлять URL через backend resource flow и быть готовым к
403или409, потому что raw MinIO path не означает доступ; - если следующая wave добавит write-сущность для permission-side assignments, она должна быть названа явно как
access assignment,bundle assignment,grants groupилиaccess profile, а не как ещё однаgroup; - identity может существовать без прикладного доступа, поэтому
unassigned/no accessдолжен рендериться как валидное состояние backend bootstrap, а не как ошибка UI; - lifecycle Keycloak-managed human accounts держится вокруг
disable/offboarding, поэтому frontend не должен возвращать delete UX как обычное действие; - tenant-aware business policy уже согласована и публикуется backend contracts block:
b2c -> organization optional,b2b/b2g -> organization required. Frontend должен читать эту policy из backend и заранее отражать requiredness там, где backend уже её enforce-ит.
Cross-repo reference points для этой wave:
- backend-ksailab:
docs/keycloak-onboarding-provisioning-flow.md - backend-ksailab:
docs/auth-capability-bootstrap-contract.md - Access groups и учебный ABAC boundary
- frontend-ksailab:
application/admin/permissionsSurfacePolicy.ts
Роль фронта в auth-схеме
Frontend не должен быть источником истины по доступам. Его задача — корректно потреблять уже рассчитанный access context.
Фронт должен
- отправлять browser login в backend entrypoint, а не напрямую в Keycloak по умолчанию;
- получать principal,
accesscontext и 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 выглядит так:
- Frontend отправляет browser на
GET /api/v1/auth/login. - Backend уводит browser в Keycloak.
- Keycloak возвращает browser на
GET /api/v1/auth/callback. - Backend выполняет callback/exchange, формирует server-side auth session и редиректит browser обратно на frontend route.
- Frontend запрашивает
GET /api/v1/auth/me. - Frontend получает:
principal;classification;realm_roles;access;bundles;effective_capabilities.
- Меню, страницы и компоненты рендерятся на основе capabilities.
- Любой реальный 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;
accessblock отдельно описываетlegacy_role,access_groupилиunassignedruntime для текущего principal;/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-outUX workaround на frontend ещё не должен приниматься за финальный продуктовый контракт.
Full platform logout
- local app session завершена;
- Keycloak SSO session тоже завершена;
- следующий заход требует реального login flow, а не silent re-login;
- именно это должно быть user-facing meaning кнопки
Выйтив KSAILab.
Что сейчас остаётся переходным
frontend-ksailabнаmainвсё ещё содержитlogged-outrecovery 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, но и уже рассчитанный набор:
access;effective_capabilities;bundles;- principal, classification и профильный контекст.
Отдельно важно: 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;- отсутствие отдельного
/capabilitiesendpoint в первой фазе; - 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 frontend должен дополнительно учитывать:
- group management source of truth —
/api/v1/groups/management*; - access-group lifecycle —
/api/v1/access-groups; permissions/groups— read-only permission metadata, не write UI;- course-like source of truth —
topics -> sections -> subsections -> tests, пока отдельного/coursesAPI нет; - no-access /
unassignedcontent, question и file actions получают403.
Текущий runtime capability baseline:
auth.bootstrap.readonboarding.teacher.createonboarding.group.createonboarding.group.assign_teacheronboarding.student.bulk_createonboarding.credentials.consume
Важно:
- create-операции больше не должны проектироваться как ответы с обычным inline
temporary_password; - frontend должен делать one-time receipt handoff;
unassigned/no accessпосле provisioning нужно считать валидным backend state до access-group assignment;- 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-outvssession-expiredи на то, что logout не превращается обратно в implicit login success; - smoke на target full platform logout после завершения companion backend/keycloak changes;
- tests, подтверждающие, что frontend не ожидает отдельный
/capabilitiesendpoint; - tests на capability-driven menu и route guards;
- tests на отсутствие premature bootstrap на public auth routes;
- tests на onboarding teacher/group/student UI flows;
- tests на одноразовый показ временного пароля через receipt consume flow.