Перейти к основному содержимому

GET /api/investors/me — Текущий инвестор (getCurrentInvestor)

Назначение

Прочитать профиль текущего аутентифицированного инвестора: ФИО, контакты и флаги безопасности (isTwoFactorEnabled, hasPassword, hasPinCode). Вызывается клиентом при инициализации сессии; ответ 401 означает недействительный токен и приводит клиент к очистке локальной сессии. Данные отдаются только владельцу записи.

Metadata

ПолеЗначение
Сервис/контекстidentity
API areapublic
Feature groupprofile
Статусapproved
Документdocs/06-services/identity/api/investor/get-current-investor.md

Актор и доступ

ПроверкаЗначение
Actor typeUser (Bearer JWT)
Auth policy / Permissionsauthenticated; permission identity.users.view-self
Scope (RBAC)self (роль не требуется)
Record-levelтолько собственная запись: UserId из claim sub
Tenant isolationне применяется (инвестор вне tenant, ADR-0052); для пользователей компаний — та же операция self
Auditread-sensitive (PII в ответе — только владельцу)

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/investors/me
Success status200
Idempotency headerне применяется (safe GET)
Correlationсквозной trace через traceparent

Входные данные / параметры запроса

Body/route/query-параметров нет.

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
UserIdidentity claims (sub)GuidДавалидный JWT (подпись, exp, notBefore)401 при отсутствии/просрочке

Модель ответа GetCurrentInvestorQueryResult

ПолеТипОбязательностьИсточникMaskingОписание
investorIdstring (Guid)ДаUserEntity.IdИдентификатор инвестора.
nameFullNameДаVO Name (firstName, lastName, middleName?)— (владелец)ФИО; lastName может быть пустой строкой (lazy-регистрация).
phoneNumberstringДаUserEntity.PhoneNumber (E.164)без маскирования (владелец)Основной логин.
emailstring?НетUserEntity.Email— (владелец)Email, если задан.
isTwoFactorEnabledboolДаUserEntity.IsTwoFactorEnabledФлаг 2FA.
hasPasswordboolДаcomputed: пароль установленхэш не раскрываетсяУстановлен ли пароль.
hasPinCodeboolДаcomputed: пин-код заданхэш не раскрываетсяЗадан ли пин-код.
createdAtstring (ISO 8601)ДаUserEntity.CreatedAtДата регистрации.

Алгоритм

  1. Auth-middleware проверяет валидность JWT до операции (иначе 401, клиент очищает локальную сессию).
  2. Получить пользователя по UserId из claim sub; при статусе Blocked/ArchivedIDENTITY_USER_BLOCKED.
  3. Отдать профиль (маппинг поле-в-поле — таблица ответа); производные hasPassword/hasPinCode вычисляются по наличию соответствующих хэшей, сами хэши в модель не читаются.
  4. Чтение из основного хранилища (read-your-writes): изменения только что завершённых self-операций (setPinCode, changePassword, confirmEmailChange) видны сразу, реплики других контекстов не задействованы.
  5. Операция read-only — гонок и переходов статуса нет. HTTP 200.

Замечания по границам:

  • lastName при lazy-регистрации может быть пустой строкой (не null) — контракт FullName (user.md); middleName отсутствует, если не задан.
  • Та же self-операция обслуживает и пользователей компаний (менеджер застройщика, агент): для них ответ несёт те же поля; tenant-фильтр не применяется (self по UserId).
Отложенный отзыв (accepted degradation)

Access-JWT сессии, по которой уже выполнен sign-out, но срок exp (≤ 15 мин) ещё не истёк, продолжает возвращать профиль — отзыв сессии на чтении профиля не проверяется (согласовано с sign-out.md; риск ограничен коротким TTL access-токена).

Предусловия и постусловия

  • Пред: запрос несёт валидный access-JWT; субъект (sub) существует. Профиль — источник истины identity, реплики не требуются.
  • Пост (успех): состояние не меняется (safe GET); клиент получил актуальный снапшот профиля и флагов безопасности. Ни события, ни аудит-записи не порождаются (кроме read-sensitive-лога факта чтения).

Acceptance Criteria

  • Given валидный JWT инвестора Active, When GET /api/investors/me, Then 200 с профилем (маппинг поле-в-поле), isTwoFactorEnabled/hasPassword/hasPinCode отражают текущее состояние, хэши пароля/пин-кода не раскрываются.
  • Given инвестор только что вызвал setPinCode, When GET /me следующим запросом, Then hasPinCode = true немедленно (read-your-writes).
  • Given lazy-регистрированный инвестор без фамилии, When GET /me, Then 200 с name.lastName = "" (пустая строка, не null), name.middleName отсутствует.
  • Given запрос без токена / с просроченным / невалидным JWT, When GET /me, Then 401 без схемы тела; клиент очищает локальную сессию и уходит на re-login.
  • Given учётка, ставшая Blocked или Archived после выдачи токена, When GET /me, Then IDENTITY_USER_BLOCKED (400).
  • Given access-JWT сессии, по которой уже выполнен sign-out, но exp ещё не истёк, When GET /me, Then 200 (отложенный отзыв — принятая деградация в окне ≤ 15 мин).

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
— (auth)401Нет/просрочен/невалиден JWTбез тела схемы (клиент очищает сессию)re-login
IDENTITY_USER_BLOCKED400Status ∈ {Blocked, Archived}«Учётная запись заблокирована»no retry

Совместимость и наблюдаемость

  • Модель используется всеми экранами личного кабинета; новые optional поля — additive; удаление/переименование — breaking (migration + rollout).
  • Logs: только UserId (без PII-значений); metrics: identity_me_requests_total; traces — OTel.

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы