# Auth Model ## Identity source Единственный источник identity: Authentik. Launcher, Plane и будущие приложения не хранят пароли пользователей и не становятся главным источником truth по логину. ## Required claims Минимальный normalized user object: ```ts type AuthUser = { sub: string; email: string; name?: string; groups: string[]; entitlements?: string[]; }; ``` Обязательные проверки: - `iss` совпадает с настроенным issuer; - `aud` совпадает с client/application audience; - `exp` не истек; - `sub` непустой и стабилен; - `email` присутствует для user-facing приложений; - `groups` или `entitlements` содержат требуемый app access. ## Groups Минимальная группа верхнего уровня: ```text nodedc:superadmin ``` Launcher: ```text nodedc:launcher:access nodedc:launcher:admin nodedc:launcher:user-manager ``` Task Manager: ```text nodedc:taskmanager:access nodedc:taskmanager:admin ``` Future apps: ```text nodedc:agents:access nodedc:tender:access nodedc:onec:access nodedc:dm:access ``` ## Access levels Уровень 1: Authentik app access. Отвечает на вопрос, можно ли открыть приложение. Уровень 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; - выполнять admin calls к Authentik API; - хранить audit log; - возвращать frontend только нормализованные данные пользователя и разрешенные действия; - не отдавать access/refresh/service tokens в browser bundle. ## 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.