03 auth lifecycle
Машина состояний пользователя: от регистрации до удаления.
Диаграмма состояний¶
stateDiagram-v2
[*] --> registration_in_progress: createOrFindPendingUser()
registration_in_progress --> kyc_pending: linkAuthUser()
registration_in_progress --> registration_expired: TTL истёк (REGISTRATION_TEMP_USER_TTL_DAYS)
kyc_pending --> pending_operator_review: confirmKycData()
pending_operator_review --> kyc_pending: operatorReject()
pending_operator_review --> active: finalizeVerification()
active --> blocked: blockUser()
blocked --> active: unblockUser()
registration_expired --> [*]: ArchiveExpiredRegistrations job (isArchived=true)
Описание статусов¶
| Статус | Описание | Блокирует вход? | Триггер перехода |
|---|---|---|---|
registration_in_progress |
Временный пользователь создан, OTP выслан, email не подтверждён | Нет (вход невозможен — auth user не привязан) | createOrFindPendingUser() |
kyc_pending |
Email подтверждён, auth user привязан; документы ещё не загружены | Нет | linkAuthUser() |
pending_operator_review |
KYC-данные отправлены, ждут проверки оператором или auto-verify | Нет | confirmKycData() |
active |
Полноправный аккаунт; кошелёк создан, KYC fully_verified |
Нет | finalizeVerification() |
blocked |
Заблокирован оператором; вход запрещён | Да → 403 | blockUser() |
registration_expired |
Регистрация не завершена за TTL; isArchived флаг не выставлен немедленно |
Да → 403 | ArchiveExpiredRegistrations job |
isArchived = trueвыставляется job'омArchiveExpiredRegistrations. После этого пользователь не находится ни одним query (where: t.isArchived.equals(false)).
AuthValidateEndpoint¶
Вызывается при каждом token refresh и endpoint-запросе через Serverpod middleware.
Статусы, блокирующие доступ (→ NotAuthorizedException / insufficientAccess):
| Статус | Поведение |
|---|---|
registration_expired |
403, логируется warning |
blocked |
403, логируется warning |
isArchived = true |
user не найден → 401 (unauthenticated) |
Возвращаемый объект ValidateResult:
| Поле | Тип | Источник |
|---|---|---|
userId |
int |
users.id |
status |
String |
users.status |
kycStatus |
String? |
kyc_verifications.status |
email |
String? |
users.email |
displayName |
String? |
Расшифровка PII: fullName → firstName lastName |
roles |
List<String> |
admin_roles JOIN admin_user_roles |
Также обновляет users.lastActivityAt при каждом успешном validate.
Файл: lib/src/endpoints/auth/auth_validate_endpoint.dart
Cleanup FutureCalls¶
Все jobs регистрируются и планируются в lib/server.dart при старте — идемпотентно (cancel + reschedule).
| Job | Расписание | Действие |
|---|---|---|
DeleteIncompleteRegistrationsFutureCall |
Через 1 мин после старта, затем само перепланируется каждые 6 ч | Удаляет registration_in_progress записи старше OTP TTL без привязанного auth user |
ArchiveExpiredRegistrationsFutureCall |
Ежедневно в 02:00 UTC | Ставит isArchived=true на пользователей со статусом registration_expired (TTL истёк) |
SendRegistrationRemindersFutureCall |
Ежедневно в 09:00 UTC | Push/email напоминание не завершившим KYC за REGISTRATION_REMINDER_DAYS_BEFORE_EXPIRY дней до экспирации |
HardDeleteArchivedUsersFutureCall |
Раз в 7 дней | Hard delete строк с isArchived=true старше REGISTRATION_HARD_DELETE_ARCHIVED_AFTER_DAYS дней |
Env vars¶
| Переменная | Умолчание | Назначение |
|---|---|---|
REGISTRATION_OTP_TTL_MINUTES |
10 |
Время жизни OTP-кода в минутах |
REGISTRATION_OTP_MAX_ATTEMPTS |
5 |
Максимум попыток ввода OTP |
REGISTRATION_OTP_RATE_LIMIT_PER_10MIN |
3 |
Максимум запросов OTP за 10 минут |
REGISTRATION_TEMP_USER_TTL_DAYS |
7 |
Дней до экспирации незавершённой регистрации |
REGISTRATION_HARD_DELETE_ARCHIVED_AFTER_DAYS |
30 |
Дней до hard-delete после архивации |
REGISTRATION_REMINDER_DAYS_BEFORE_EXPIRY |
2 |
За сколько дней до экспирации слать напоминание |
Все значения читаются в RegistrationConfig.initialize() при старте сервера.
Файл: lib/src/services/registration_service.dart