Перейти к содержанию

Auth

Аутентификация: JWT, OTP, token refresh, nginx auth_request.

Компоненты

Класс Файл Назначение
EmailIdpEndpoint src/auth/email_idp_endpoint.dart Переопределяет хуки email IDP: логирует события, шлёт push-уведомления
AuthValidateEndpoint src/endpoints/auth/auth_validate_endpoint.dart Валидирует JWT при refresh, возвращает ValidateResult
NginxAuthRoute src/web/routes/nginx_auth_route.dart HTTP-эндпоинт /nginx/auth для auth_request nginx (мобильный пользователь)
NginxAdminAuthRoute src/web/routes/nginx_auth_route.dart HTTP-эндпоинт /nginx/auth/admin для Admin Panel
JwksRoute src/web/routes/jwks_route.dart Отдаёт JWKS для верификации launch-JWT мини-приложениями
AuthEventService src/services/auth_event_service.dart Запись auth-событий в audit log

JWT Flow

sequenceDiagram
  App->>AC: login(email, password)
  AC->>App: accessToken (TTL 30 мин) + refreshToken (TTL 14 дней)
  Note over App: хранится в flutter_secure_storage
  App->>AC: любой RPC (JWT в заголовке)
  AC->>AC: verify JWT → resolve userId
  App->>AC: refreshTokens() (по истечении accessToken)
  AC->>App: новые токены

Значения TTL из server.dart: - accessTokenLifetime: Duration(minutes: 30) — явно задан (Serverpod default: 10 мин) - refreshTokenLifetime — не задан, используется default Serverpod 14 дней

AuthValidateEndpoint

Вызывается при каждом refreshTokens() со стороны клиента. Последовательность:

  1. session.authenticated.userIdentifierauthUserId (UUID)
  2. Поиск User по authUserId, фильтр isArchived=false
  3. Блокирующие статусы: registration_expired / blockedNotAuthorizedException(insufficientAccess)
  4. Загружает KycVerification (для kycStatus)
  5. Загружает UserProfile (для displayName из расшифрованного encryptedPii)
  6. Обновляет lastActivityAtDateTime.now()
  7. Загружает admin-роли через admin_user_roles JOIN admin_roles (graceful fallback [] до миграции)
  8. Логирует session_refresh событие через AuthEventService
  9. Возвращает ValidateResult(userId, status, kycStatus, email, displayName, roles)

NginxAuthRoute

Путь: GET /nginx/auth. Используется nginx как auth_request.

Код Условие Заголовки в ответе
200 JWT валиден, пользователь активен и не заблокирован X-User-Id, X-User-Status, X-User-Email
401 JWT отсутствует / невалиден / пользователь не найден
403 status=registration_expired или user_profile.isLocked=true

Блокировка проверяется через UserProfile.isLocked, а не через поле User.

NginxAdminAuthRoute

Путь: GET /nginx/auth/admin. Требует admin-роль из admin_user_roles.

Код Условие Заголовки в ответе
200 JWT валиден + роль из разрешённого набора X-User-Id, X-User-Email, X-User-Role
401 JWT отсутствует / невалиден / пользователь не найден
403 status=registration_expired / нет роли / роль не в whitelist

Разрешённые роли: superadmin, operator, finance, support.

Graceful fallback: если таблицы admin_roles/admin_user_roles ещё не созданы (до миграции A-1), возвращает 403.

JwksRoute

  • Путь: GET /jwks.json
  • Содержимое: результат LaunchJwtService.jwks() — публичный JWKS для верификации launch-JWT
  • Cache-Control: public, max-age=86400 (24 часа)
  • Мини-приложения кешируют ключи 24 часа, используют kid из заголовка токена при ротации ключей

EmailIdpEndpoint

Переопределяет следующие хуки базового класса EmailIdpBaseEndpoint:

Хук Действия
login(email, password) Вызывает super.login(), затем: логирует sign_in событие (success/fail), шлёт push "Вход в аккаунт" (канал security, локаль из Accept-Language)
finishRegistration(registrationToken, password) Вызывает super.finishRegistration(), логирует register событие
startPasswordReset(email) Вызывает super.startPasswordReset(), логирует password_reset_request
finishPasswordReset(token, newPassword) Вызывает super.finishPasswordReset(), логирует password_reset_complete, шлёт push "Пароль изменён"

Push при login: MVP без детектора нового устройства — детектор запланирован на Phase 2.

При ошибке login: EmailLoginServerException перехватывается, событие логируется с success=false и failureReason, после чего исключение rethrow.