# Auth Model

## Identity и control-plane source

Пользовательский и административный источник истины: Launcher backend.

Launcher владеет продуктовыми сущностями платформы: клиенты/компании, участники, роли клиента, группы клиента, инвайты, доступы к сервисам, audit log и пользовательский профиль платформы.

Authentik остается внутренним identity/session слоем. Он хранит техническую identity, password/session/MFA/OIDC и получает из Launcher только синхронизированную проекцию, нужную для SSO: пользователей, enrollment/reset flows, группы/entitlements и профильные claims.

Пользователь и обычный администратор не должны видеть бренд Authentik в платформенном UI. Прямой доступ к Authentik допускается только через отдельную внутреннюю/служебную ссылку для системной настройки.

Plane и будущие приложения не хранят пароли пользователей и не становятся главным источником truth по платформенному пользователю. Они принимают OIDC/session claims и связывают их со своими локальными доменными моделями.

## User journey

Целевой production flow:

```text
nodedc.ru marketing site
  -> Войти на платформу
  -> platform login
  -> NODE.DC Launcher
  -> application tiles
  -> Task Manager / future apps
```

UI платформы не должен показывать пользователю название identity-провайдера. В пользовательских кнопках и текстах используется нейтральное "Войти", "Вход на платформу", "Сессия NODE.DC".

Прямые ссылки на приложения остаются допустимым пользовательским сценарием. Если session нет, приложение или внешний proxy/auth layer должен увести пользователя в login flow и после успешного входа вернуть к исходному приложению.

## Platform login UX

Текущий hosted Authentik login допустим только как dev/runtime bootstrap для проверки OIDC, JWKS, session и app redirect.

Production login должен быть NODE.DC-branded:

- пользователь видит только NODE.DC экран входа;
- тексты, ошибки, recovery/enrollment и logout не светят бренд Authentik;
- Authentik остается внутренним password/session/MFA/OIDC слоем;
- пароль, service tokens и refresh tokens не попадают во frontend bundle;
- прямые ссылки на Launcher/Task Manager/future apps ведут через этот же NODE.DC login facade и возвращают пользователя в исходное приложение.

Допустимые реализации:

1. Кастомизированный Authentik flow, если его можно привести к NODE.DC UX без компромиссов по дизайну.
2. Launcher/BFF login facade, который server-side вызывает внутренний Authentik flow/API и сохраняет безопасность IdP.

Запрещено делать быстрый frontend-only password form, который обходит IdP protections, хранит секреты в браузере или ломает MFA/recovery/audit.

Текущее безопасное решение зафиксировано в `docs/AUTH_BRANDED_LOGIN_RFC.md`: сначала используем Authentik-native Brand/CSS/Flow customization. Reverse proxy HTML-rewrite, password form в Launcher и пересылка пароля через BFF запрещены.

## Inter-app session sync status 2026-06-23

Источник истины по пользовательской сессии остаётся в Launcher/Auth/Authentik chain. Приложения не должны выдавать себе права из локального browser-event или marker-а; любые live-события session sync являются только сигналом для revalidation через платформенный authority.

Фактический общий live-контур до BIM был logout-only:

- Launcher `/auth/session-sync` публиковал `nodedc:session:logout` в соседние приложения через iframe/BroadcastChannel/localStorage/frontchannel;
- Engine helper `src/platform/auth/sessionSync.ts` знает только `nodedc:session:logout`;
- Tasker/OPS имеет отдельный frontchannel/internal logout контур, но login/change parity не оформлен как общий protocol;
- login live promotion открытого приложения после входа в другом surface не был закрытым platform-wide контрактом.

BIM Viewer 23.06.2026 добавил BIM-first login-sync slice для share-link сценария:

- Launcher service registry получил `loginSyncUrl` для BIM;
- после успешного login Launcher публикует `nodedc:session:login` и делает frontchannel вызов зарегистрированных login-sync endpoints;
- BIM `/auth/login-sync` создаёт короткоживущий server-side marker;
- BIM `/api/auth/session` возвращает `loginSyncEventId`, но сам marker не является auth-token;
- BIM frontend по новому marker-у запускает обычный optional-launch/handoff через Launcher, где происходит нормальная проверка сессии и доступа;
- BIM logout приведён к существующему logout canon и должен live-понижать controls до guest/limited view без ручного refresh.

Граница текущей реализации: это кандидат на общий login-sync contract, а не закрытый канон для всей платформы. Перед подключением следующего сервиса нужно оформить `ndcauth.session` protocol: `login`, `logout`, `change`, payload, origin checks, TTL/nonce/correlation, service registry fields, revalidation rules, audit/observability и browser e2e matrix по HUB/OPS/ENGINE/BIM.

Правило для новых сервисов: не добавлять частный per-app login/logout bus. Сервис регистрирует sync endpoints в Launcher/control-plane registry, принимает event только как invalidate/promote trigger и всегда перепроверяет доступ через backend authority.

## Required claims

Минимальный normalized user object:

```ts
type AuthUser = {
  sub: string;
  email: string;
  name?: string;
  avatarUrl?: string | null;
  groups: string[];
  entitlements?: string[];
};
```

Обязательные проверки:

- `iss` совпадает с настроенным issuer;
- `aud` совпадает с client/application audience;
- `exp` не истек;
- `sub` непустой и стабилен;
- `email` присутствует для user-facing приложений;
- `name`, `picture`/`avatar_url` используются как единый профильный контекст, но мастер-данные профиля должны приходить из Launcher;
- `groups` или `entitlements` содержат требуемую техническую app access projection.

## Groups

Минимальная группа верхнего уровня:

```text
nodedc:superadmin
```

Launcher:

```text
nodedc:launcher:admin
nodedc:launcher:user
```

Task Manager:

```text
nodedc:taskmanager:admin
nodedc:taskmanager:user
```

Future apps:

```text
nodedc:agents:access
nodedc:tender:access
nodedc:onec:access
nodedc:dm:access
```

## Access levels

Уровень 1: Launcher access model.

Отвечает на вопрос, можно ли открыть приложение. Источник решения — данные Launcher: клиент, членство, группы клиента, user grants, deny exceptions, статус сервиса и период доступа.

Authentik app access является технической проекцией этого решения для SSO/enforcement, а не местом ручного бизнес-администрирования.

Launcher показывает каталог приложений как витрину, а не скрывает все недоступные плитки. Для приложения без доступа кнопка перехода отключена и отображается состояние "Нет доступа". Это важно для продаж, onboarding и понимания доступных модулей платформы.

Уровень 2: Launcher admin roles.

Отвечает на вопрос, можно ли управлять пользователями, группами, app registry и audit.

Уровень 3: Application roles.

Например, Plane workspace owner/admin/member/viewer. Эти роли остаются внутри Plane.

## Launcher backend requirements

Backend должен:

- хранить Authentik service token только server-side;
- хранить клиентов, пользователей, членства, группы, инвайты, grants/exceptions и профиль платформы;
- выполнять server-side sync в Authentik API без раскрытия Authentik пользователю;
- создавать invite/enrollment flows из Launcher UI;
- хранить audit log;
- возвращать frontend только нормализованные данные пользователя и разрешенные действия;
- не отдавать access/refresh/service tokens в browser bundle.

## Live control-plane data rule

Демо-пользователи Launcher допустимы только как fixture на этапе frontend MVP.

Как только появляется backend/admin API, Launcher control-plane должен перейти на живой seed:

- реальные платформенные пользователи вместо `root@nodedc.local`, `ivan@romashka.ru` и прочих декоративных участников;
- явный superadmin NODE.DC;
- реальные клиентские пользователи и группы, подтвержденные по Plane/Auth/Launcher данным;
- idempotent seed/migration script с backup текущего `launcher-data.json`;
- отсутствие прямого изменения Plane users/workspace/task данных из Launcher seed;
- все дальнейшие проверки клиентов, групп, инвайтов и доступов выполняются через Launcher admin UI.

Если пользователь существует в Plane, но еще не существует в Authentik, он должен быть заведен через Launcher/Auth sync flow, а не ручным рассинхроном.

## Plane identity link

Минимальная таблица или эквивалентная модель в Plane:

```text
external_identity_link
  provider = authentik
  authentik_sub
  email
  plane_user_id
  created_at
  last_login_at
  status
```

Правило миграции:

- сначала искать link по `authentik_sub`;
- если link найден, логинить связанный `plane_user_id`;
- если link не найден, но email совпадает и включен migration auto-link, создать link;
- если доступа нет, вернуть deny.

## Plane standalone compatibility

Plane является подключаемым приложением NODE.DC, но не должен становиться технически невынимаемым модулем.

Правила интеграции:

- OIDC NODE.DC добавляется как отдельный auth provider path, а не как замена всей Plane auth системы;
- стандартные Plane session/API-token возможности сохраняются, пока нет отдельного решения их убрать;
- Launcher хранит только платформенные привязки и access projection, но не владеет Plane workspace/project/task/comment;
- связь с Plane строится через external identity link и будущий service adapter/API, а не через прямое изменение Plane DB из Launcher;
- env-флаги должны позволять включать NODE.DC SSO в составе платформы и выключать его для standalone-поставки Plane.
