Сценарий: регистрация и login B2B/B2C

NOTE

Статус: Target design. Документ описывает целевую доменную модель. Соответствующий код реализован частично (см. backend/internal/core/) или пока не начат. Правила маркировки — в 50-processes/documentation-standard.md.

Принцип: используем shared/auth, не дублируем

Аутентификация (identity, JWT, refresh, RBAC, signup, профиль, удаление) полностью делегирована общему сервису /Users/belkanov/projects/shared/auth/ (auth-service). Tracium не реализует свои password hash, JWT issuance, refresh rotation, OAuth grant types — это делает auth-service. Tracium подписывается на его доменные события и расширяет профиль User бизнес-полями (B2B/B2C, pricing group, contracts, supplier credentials).

Любая функциональность, которой нет в shared/auth (см. секцию «Что расширяем»), реализуется внутри shared/auth через PR — а не дублируется в Tracium.

Терминология (выравнивание)

Tracium-понятиеshared/auth-понятиеГде живёт
Customer (бизнес-агрегат)User (model/user.go) + Tracium-extensionUser.Id — общий ключ
AuthIdentitylogin_password / messenger / system / (TBD: oauth, sso, api_key)shared/auth
SessionJWT cookies: access_token (~1ч) + jwt-refresh (~7д)shared/auth
SessionContext (Tracium VO)JwtCustomClaims (Id, Roles, RolesUpdatedAt) + Tracium customer profile lookupcomposed на API gateway
RoleRole{Id, Name, DisplayName, Permissions}shared/auth
PermissionPermission{Resource, Action}shared/auth
Token invalidation на смене ролейRolesUpdatedAt Unix timestamp в JWT claimsshared/auth
Anonymous visitorJWT с level: anonymousshared/auth
M2M-сервисsystem role + GenerateM2MToken (pkg/jwt)shared/auth

Участники

BC / сервисРоль
shared/authOwner identity / sessions / JWT / RBAC / signup / профиль / удаление аккаунта.
Customer (Tracium)Owner бизнес-профиля: customer_type, legal_info, customer_pricing_group, contracts, supplier_credentials[]. Подписан на auth_user_events.
VisibilityИспользует roles из JWT + customer_pricing_group как Subject.
PricingПодписан на CustomerPricingGroupChanged (Tracium event) для invalidate cache.
CredentialsПодготовка per-customer DEK при первой связи User → Customer.
ExternalOAuth/SSO провайдеры (TBD-расширение в shared/auth), Telegram/Max боты (есть), SMS/email gateway (есть).

Endpoints из shared/auth (используем как есть)

EndpointНазначение
POST /signup/phone/token/signup/phone/confirmPUT /signup/phone3-шаговая регистрация по телефону + SMS-код + пароль
POST /signup/email/token/signup/email/confirmPUT /signup/email3-шаговая по email
POST /signup/system (требует system role JWT)Идемпотентная регистрация B2B без верификации (для интеграций)
POST /login (grant_type=password)Универсальный login. Логин = email если есть @, иначе телефон.
POST /login (grant_type=authorization_code)OAuth code exchange
POST /login (grant_type=refresh_token)Refresh
GET/POST /auth/refreshЯвный refresh
GET/POST /logoutОчистка JWT cookies, anonymous token
GET /auth/nginxInternal: nginx auth_request — валидация JWT, refresh, проброс заголовков
POST /login/messenger/token/login/messenger/verifyLogin через Telegram/Max бот (кодом)
POST/DELETE/GET /messenger/link*Привязка / отвязка messenger-аккаунта
GET /profile / PATCH /profile / DELETE /profileПрофиль (под Nginx auth)
POST /admin/users/{id}/roles / DELETE /admin/users/{id}/rolesУправление ролями (триггерит RolesUpdatedAt)
GET /admin/roles / POST /admin/roles / PUT /admin/roles/{name}Справочник ролей

Доменные события shared/auth (топик auth_user_events)

СобытиеКогдаTracium consumer
user.profile.updatedPATCH /profileCustomer BC: sync display_name / email / phone в Tracium-extension
user.messenger.linkedПривязан messengerCustomer BC: для UX-уведомлений
user.messenger.unlinkedОтвязан messengerТо же
user.deletedDELETE /profile (или admin)Customer BC: запуск Tracium-grace PersonalDataDeletionRequested

Доменные события Tracium (топик customer.events.v1)

Публикуются Customer BC поверх событий shared/auth — отражают бизнес-смысл, которого нет в auth.

СобытиеКогда
КлиентРасширенЗарегистрирован (CustomerProfileCreated)Tracium увидел нового User.Id (через auth_user_events или first-touch) и создал Tracium-extension
ЮридическиеДанныеПодтверждены (LegalInfoVerified)Verified ИНН (B2B)
ГруппаТарификацииНазначена (CustomerPricingGroupChanged)Operator / promo / contract
ДоговорПодписан (ContractSigned)B2B
КастомнаяФичаПереключена (CustomerFeatureToggled)Operator / self-service
ПерсональныеДанныеУдалены (PersonalDataDeleted)После 30 дней grace (152-ФЗ)

Sequence — B2C регистрация по email через shared/auth

sequenceDiagram
    autonumber
    participant V as Visitor
    participant API as Tracium API gateway
    participant AUTH as shared/auth
    participant SMTP as Email gateway
    participant K as Kafka (auth_user_events)
    participant C as Customer (Tracium)

    V->>API: POST /signup/email/token {email}
    API->>AUTH: proxy
    AUTH->>SMTP: send confirmation code
    AUTH-->>V: 200
    V->>API: POST /signup/email/confirm {email, code}
    API->>AUTH: proxy
    AUTH-->>V: 200 + signup_token
    V->>API: PUT /signup/email {signup_token, password, name}
    API->>AUTH: proxy
    AUTH-->>AUTH: User создан, JWT cookies set
    AUTH-->>K: user.profile.updated (initial)
    K-->>C: consume
    C->>C: создать Tracium-extension (customer_type=individual, pricing_group=b2c_retail)
    C-->>C: КлиентРасширенЗарегистрирован
    AUTH-->>V: 200 + cookies (access_token + jwt-refresh)

Sequence — B2B регистрация (system signup от интегратора)

sequenceDiagram
    autonumber
    participant SVC as B2B integrator (M2M)
    participant API as Tracium API gateway
    participant AUTH as shared/auth
    participant K as Kafka (auth_user_events)
    participant C as Customer (Tracium)
    participant INN as ИНН-verify

    SVC->>SVC: GenerateM2MToken (system role)
    SVC->>API: POST /signup/system {email, name, password?, ...}
    API->>AUTH: proxy + JWT system
    AUTH->>AUTH: SystemRoleMiddleware ok
    AUTH-->>AUTH: User создан, SystemRegistered=true
    AUTH-->>K: user.profile.updated
    K-->>C: consume
    C->>C: Tracium-extension {customer_type=legal_entity, pricing_group=b2b_standard}
    C->>INN: verify ИНН (async)
    INN-->>C: result
    alt verified
        C-->>C: ЮридическиеДанныеПодтверждены
    else
        Note over C: pending verification, alert operator
    end
    AUTH-->>SVC: 200 + user object

Sequence — login (password grant)

sequenceDiagram
    autonumber
    participant U as Customer
    participant API as Tracium API gateway
    participant AUTH as shared/auth
    participant NGX as Nginx auth_request
    participant C as Customer (Tracium)

    U->>API: POST /login (grant_type=password, username, password)
    API->>AUTH: proxy
    AUTH->>AUTH: validate password (auto-detect email vs phone)
    alt success
        AUTH-->>U: cookies access_token + jwt-refresh
    else fail
        AUTH-->>U: 401
    end
    U->>API: GET /api/... (next request)
    API->>NGX: auth_request /auth/nginx
    NGX->>AUTH: GET /auth/nginx with cookies
    AUTH-->>NGX: 200 + headers (X-User-Id, X-User-Roles, X-Roles-Updated-At)
    NGX-->>API: ok + headers
    API->>C: lookup Tracium-extension by User.Id
    C-->>API: customer_type, pricing_group, features
    API->>API: build SessionContext = JwtCustomClaims + Tracium profile

Sequence — login через messenger (Telegram/Max)

sequenceDiagram
    autonumber
    participant U as Customer
    participant BOT as Telegram/Max bot
    participant AUTH as shared/auth

    U->>BOT: /login команда
    BOT->>AUTH: POST /login/messenger/token {messenger_user_id}
    AUTH-->>BOT: code (6 digits)
    BOT-->>U: «введи код в UI»
    U->>AUTH: POST /login/messenger/verify {code}
    AUTH-->>U: cookies access_token + jwt-refresh

Что Tracium расширяет в shared/auth (нужны PR в auth-service)

Перечисленное отсутствует в текущем shared/auth и должно быть добавлено именно туда (а не реализовано в Tracium):

РасширениеЗачемКуда добавлять
OAuth провайдеры (Yandex / Google / VK)B2C ускоренный onboarding — массовый запросновый grant_type oauth_provider + /login/oauth/{provider}/callback
SAML / OIDC SSOB2B enterprise клиенты/auth/sso/{org} + IdP-mapping
MFA / TOTP / Recovery codesB2B admin, suspicious IPUser.MfaSecret (encrypted) + /mfa/setup / /mfa/verify / recovery codes
Account lockout (5 fails / 10 мин)Brute force защитаrate limiter middleware на /login per (username, ip)
Suspicious IP detection + require_mfa flagAdaptive authновое поле в JwtCustomClaims + middleware
Session TTL policy per-customerB2B 12ч, B2C 30 дней — разныеUser.SessionTtlOverride или role-based config
Customer-managed API keys (B2B integrations)Не путать с M2M-system-токеномApiKey entity + /admin/users/{id}/api-keys + новый grant api_key
Personal data deletion с grace (152-ФЗ)Сейчас DELETE /profile сразу + user.deletedpromotion: user.deletion.requested (T0) → user.deletion.confirmed (T+30д) → user.deleted
auth_audit eventsLogin attempts, failures, MFA challengesновый kafka topic auth_audit_events
Block / unblock with reasonCompliance + операторские действиярасширить Disabled в {disabled, reason, until}

Каждое — отдельный RFC + PR в shared/auth. После merge — Tracium реализует consumer-side (новые подписки в Customer BC).

Что Tracium не просит у shared/auth (живёт у нас)

  • Customer.customer_type, legal_info, customer_pricing_group, contract, payment_profiles, delivery_addresses, features.
  • supplier_credentials[] (целиком Credentials BC).
  • Subject для Visibility (composed из roles + pricing_group).
  • Бизнес-роли «закупщик / руководитель / аналитик» — тоже в shared/auth Role справочнике; Tracium только использует.
  • Sessions list для UX (revoke all) — может остаться в shared/auth, Tracium не дублирует.

Decision points

  • 5 failed логинов за 10 мин → отвечает shared/auth (расширение TBD: lockout). Tracium не вмешивается.
  • suspicious IP / новый device → MFA → расширение в shared/auth (TBD).
  • B2B admin role → MFA always → расширение (см. таблицу выше).
  • B2B SSO без mapping pricing_group → fallback на b2b_standard + alert оператору Tracium.
  • User.Id уже существует, но Tracium-extension нет → first-touch creates Tracium-extension с дефолтами.
  • Email уже existed с другим identity → правило shared/auth: signup идемпотентен по (phone, email) — возвращает existing User. Tracium не меняет это поведение.

Edge cases

СлучайПоведение
Customer удалён через DELETE /profile (shared/auth)user.deleted → Tracium запускает PersonalDataDeletionRequested → 30 дней grace → PersonalDataDeleted (Tracium-extension физически удаляется, supplier_credentials → ротация DEK + revoke).
RolesUpdatedAt обновился (admin изменил роли)Старый JWT станет stale при следующем /auth/nginx → forced refresh. Tracium pricing/visibility caches invalidate по (User.Id).
Recovery codes не работают (TBD-расширение отсутствует)Сейчас reset через email/phone signup workflow shared/auth (3 шага). После расширения — recovery codes.
Customer вводит OAuth provider, которого нет в shared/authUX «временно недоступно» + offer email/phone fallback. После добавления — full flow.
/auth/nginx отдал refresh (cookies обновились в pre-response)Tracium API gateway проксирует Set-Cookie клиенту. Никакого Tracium-state.
system-token истёк у B2B integratorIntegrator перегенерирует GenerateM2MToken (shared lib pkg/jwt).

Инварианты сценария

  1. Tracium никогда не выпускает свои JWT для customer’ов. Только shared/auth.
  2. Tracium никогда не хранит User.Password или TOTP-секреты. Только shared/auth.
  3. User.Id (UUID) — единственный stable ключ между shared/auth и Tracium-extension.
  4. Tracium Customer aggregate не существует без User.Id. Регистрация всегда начинается с shared/auth.
  5. Любое поле User, которое Tracium хочет читать (Name, Email, Phone, Roles), читается из JWT claims или через GET /profile / /internal/user/... — не дублируется в Tracium PG.
  6. Tracium-extension — append-only по lifecycle событиям; материализован в customer_directory PG для админки.
  7. Изменения, нужные Tracium для auth-механики → PR в shared/auth (см. таблицу расширений). Не в Tracium.

Метрики и observability

Tracium-метрики (только Tracium-side):

  • customer_extension_created_total{type} — после consume auth_user_events.
  • customer_pricing_group_changes_total{from, to}.
  • customer_personal_data_deletions_total{stage}requested / confirmed / deleted.
  • customer_legal_info_verifications_total{result}.
  • auth_event_consume_lag_seconds — задержка от shared/auth event до Tracium-extension update.

Auth-метрики (/login, /signup/*, lockouts, MFA) — собираются shared/auth, дашборд там же.

Связанные файлы