⟨/⟩ 55_Backend/Handlers/Auth/LoginByPasswordHandler.cs

56 строк · в начало

using System;
using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd;
using Ban.Sdaid.Icd.Api.Endpoints.Auth.LoginByPassword;
using Ban.Sdaid.Icd.Domain.Access;

namespace Ban.Sdaid.Icd.Backend.Handlers.Auth
{
    /// <summary>Handler входа по логину и паролю.</summary>
    public class LoginByPasswordHandler : IHandlerDocument, IFolder<BackendFolder>, IHasStructuralLinks
    {
        public SdaidLink[] Links => new[]
        {
            Rel.Uses<Ban.Sdaid.Icd.Domain.BC_Icd>(),
            Rel.Uses<UserSpec>(),
            Rel.Uses<UserTokenSpec>(),
            Rel.Uses<DOC_LoginByPassword>(),
        };

        public static string _refName = "Handler LoginByPassword";

        public string Name => "Handler: LoginByPassword";

        public string Description =>
            @"Что произойдёт при вызове endpoint LoginByPassword: сверить кредиты, выписать JWT с уникальным jti и учесть активный токен.";

        public string Version => "0.1";
        public string Status => "draft";
        public string[] Comments => new string[0];

        public static string Purpose = $"""
            ## Назначение

            Канонический сценарий обработки {SdaidAnchors.RefTo<DOC_LoginByPassword>()}
            (`{nameof(LoginByPasswordCommand)}`). Единственная ручка **без проверки права** — помечена
            `[AllowAnonymous]`, иначе войти было бы нечем.
            """;

        public static string Scenario = $"""
            ## Сценарий вызова

            1. Найти пользователя ({SdaidAnchors.RefTo<UserSpec>()}) по `{nameof(LoginByPasswordCommand.Login)}`.
               Не найден — `401` (не раскрывая, логин или пароль неверны).
            2. Сверить `{nameof(LoginByPasswordCommand.Password)}`. На этапе разработки — как открытый текст
               (хеш отложен, см. ADR-SRV-006). Не совпал — `401`.
            3. Сгенерировать `jti` (UUID v7) и JWT (несёт `jti`, `IcdUserId`, `IcdUserLogin`); срок жизни — из
               конфигурации.
            4. Записать активный токен ({SdaidAnchors.RefTo<UserTokenSpec>()}: `Jti`, `IssuedAt`, `ExpiresAt`);
               заодно подчистить истёкшие активные записи.
            5. Вернуть `{nameof(LoginByPasswordResult)}` с токеном в `{nameof(LoggedInPayload)}`.

            Права здесь не проверяются (вход). Формирование контекста и claims — при последующих запросах.
            """;
    }
}