AIDeskLab.AuthClient 1.1.7

AIDeskLab.AuthClient

Клиентская библиотека NuGet для интеграции сервисов экосистемы AIDeskLab с центральным сервером аутентификации/авторизации AuthServer через gRPC.

Целевой framework: .NET 8.0. Распространяется как пакет AIDeskLab.AuthClient.

Этот документ — единая точка входа для AI‑агентов и разработчиков команд AIDeskLab при подключении пакета. Все примеры соответствуют актуальному публичному API библиотеки.


Содержание


Возможности

Аутентификация и авторизация

  • Логин пользователей по логину/паролю, Telegram, телефону + Telegram, API‑ключам сервисов.
  • Двухфакторная аутентификация (2FA) с завершением через CompleteTwoFactorAuthAsync.
  • Refresh access/refresh токенов (включая отдельный RefreshServiceTokenAsync для S2S).
  • Runtime‑валидация JWT, проверка прав через ITokenValidationService.

Регистрация

  • Универсальная воронка заявок: Telegram, Web, Mobile, API.
  • Workflow одобрения/отклонения, верификация email, просмотр статусов.

Управление пользователями и оргструктурой

  • Пользователи, профили, Telegram аккаунты.
  • Системные и организационные роли, разрешения.
  • Организации (иерархия, 2FA политики), отделы, должности, сотрудники, рабочие команды, физические лица.
  • Назначения пользователей (UserAssignment).

Управление сервисами

  • Регистрация и сопровождение сервисов, выпуск API‑ключей.
  • Модули сервисов и связи модуль ⇄ сервис.
  • Межсервисные разрешения (ServiceToServicePermission) и пользователи, доступные сервису.

Контрагенты

  • CRUD контрагентов и контактов, юрисдикционные данные, миграция контактов в Person.

Логи и аналитика

  • Доступ к логам по пользователям/сервисам, статистика, топ активности, статистика ошибок.

Инфраструктура

  • gRPC поверх Grpc.Net.Client (HTTP/2, опционально SSL/TLS).
  • AutoMapper для маппинга proto ↔ модели (валидация конфигурации в DEBUG).
  • Хранилища токенов: InMemory, File, Custom (SystemKeyStore зарезервирован).
  • Авто‑refresh, политики повторов (Fixed/Linear/ExponentialBackoff с jitter).
  • Прозрачная межсервисная авторизация: DelegatingHandler подключается ко всем HttpClient и middleware валидации входящих S2S запросов с локальным MemoryCache.
  • Отдельный middleware валидации JWT для классических Web API.

Установка

dotnet add package AIDeskLab.AuthClient

Через Package Manager Console:

Install-Package AIDeskLab.AuthClient

Зависимости пакета (ключевые): Grpc.Net.Client, Google.Protobuf, AIDesk.AuthServer.Contracts, Microsoft.AspNetCore.Authentication.JwtBearer, Microsoft.Extensions.Caching.Memory, AutoMapper.

Поддерживаемые ОС: Windows / macOS / Linux (для условной компиляции определены символы WINDOWS, MACOS, LINUX).


Быстрый старт

1. ASP.NET Core (Web API)

using AuthClient.Extensions;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddAuthClient(builder.Configuration);

var app = builder.Build();

app.UseAuthClient();

app.UseAuthorization();
app.MapControllers();
app.Run();

UseAuthClient() подключает middleware межсервисной авторизации (ServiceToServiceAuthMiddleware). Если ваш сервис принимает входящие S2S вызовы — вызов обязателен.

Минимальная регистрация без IConfiguration:

builder.Services.AddAuthClient("https://auth-server.example.com", "my-service-id");

Регистрация через делегат:

builder.Services.AddAuthClient(options =>
{
    options.ServerUrl = "https://auth-server.example.com";
    options.ServiceId = "my-service-id";
    options.ApiKey = builder.Configuration["AuthClient:ApiKey"];
    options.ApiSecret = builder.Configuration["AuthClient:ApiSecret"];
    options.EnableServiceToServiceAuth = true;
});

2. Generic Host / Worker / Console (non‑web)

UseAuthClient() к IApplicationBuilder относится только к ASP.NET Core. В non‑web хостах используйте сервисы напрямую через DI:

using AuthClient.Extensions;
using AuthClient.Services.Interfaces;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        services.AddAuthClient(context.Configuration);
        services.AddHostedService<PermissionProbeWorker>();
    })
    .Build();

await host.RunAsync();

public sealed class PermissionProbeWorker : BackgroundService
{
    private readonly ITokenValidationService _tokenValidation;

    public PermissionProbeWorker(ITokenValidationService tokenValidation)
        => _tokenValidation = tokenValidation;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var check = await _tokenValidation.CheckPermissionAsync(
            userId: "user-id",
            serviceId: "my-service-id",
            permissionCode: "users.read",
            cancellationToken: stoppingToken);

        Console.WriteLine($"Permission granted: {check.HasPermission}");
    }
}

3. Конфигурация appsettings.json

{
  "AuthClient": {
    "ServerUrl": "https://auth-server.example.com",
    "ServiceId": "my-service-id",
    "UseSsl": true,
    "OperationTimeout": "00:00:30",

    "ApiKey": "service-api-key",
    "ApiSecret": "service-api-secret",
    "ServiceCode": "my-service",
    "EnableServiceToServiceAuth": true,
    "LocalCacheExpiry": "00:15:00",
    "MaxLocalCacheSize": 10000,

    "TokenStorage": {
      "StorageType": "InMemory",
      "EncryptTokens": true,
      "EncryptionKey": "your-encryption-key-at-least-32-chars",
      "CacheDuration": "01:00:00"
    },

    "TokenRefresh": {
      "EnableAutoRefresh": true,
      "RefreshCheckInterval": "00:01:00",
      "RefreshBeforeExpiration": "00:05:00",
      "MaxRefreshAttempts": 3,
      "RefreshRetryDelay": "00:00:05",
      "RefreshOnFirstUse": true
    },

    "Retry": {
      "EnableRetry": true,
      "MaxRetryAttempts": 3,
      "BaseDelay": "00:00:01",
      "MaxDelay": "00:00:30",
      "Strategy": "ExponentialBackoff",
      "BackoffMultiplier": 2.0,
      "UseJitter": true,
      "MaxJitterPercent": 20
    }
  }
}

Секреты (ApiKey, ApiSecret, EncryptionKey) храните в Secret Manager / Key Vault, а не в репозитории.


Конфигурация

AuthClientOptions

Корневые настройки клиента (AuthClient.Configuration.AuthClientOptions).

Параметр Тип Обязательный По умолчанию Описание
ServerUrl string Да URL gRPC endpoint AuthServer
ServiceId string Да ID текущего сервиса (используется как идентификатор в S2S и runtime‑проверках)
OperationTimeout TimeSpan Нет 30 сек Таймаут операций
UseSsl bool Нет true Использовать SSL/TLS для gRPC
ApiKey string? Нет null API‑ключ сервиса для LoginServiceAsync и админ‑операций
ApiSecret string? Нет null API‑секрет сервиса (хранить в секретах)
ServiceCode string? Нет null Код сервиса (используется при авторизации)
EnableServiceToServiceAuth bool Нет true Включить автоматическое добавление S2S заголовков ко всем HttpClient
LocalCacheExpiry TimeSpan Нет 15 мин TTL локального кэша межсервисных проверок
MaxLocalCacheSize int Нет 10000 Лимит записей локального кэша
TokenStorage TokenStorageOptions Нет дефолт Настройки хранилища токенов
TokenRefresh TokenRefreshOptions Нет дефолт Настройки авто‑refresh
Retry RetryOptions Нет дефолт Настройки повторных попыток

Validate() вызывается автоматически после биндинга и кидает ArgumentException, если ServerUrl/ServiceId пустые или таймаут невалиден.

TokenStorageOptions

Параметр Тип По умолчанию Описание
StorageType TokenStorageType InMemory InMemory / File / SystemKeyStore (TODO) / Custom
EncryptTokens bool true Шифровать токены при сохранении
EncryptionKey string Ключ шифрования (≥ 32 символа), обязателен при EncryptTokens=true
FilePath string Путь к файлу для StorageType=File
KeyPrefix string "AuthClient" Префикс ключей в системном хранилище (когда будет реализовано)
CacheDuration TimeSpan 1 час TTL кэша токенов в памяти

SystemKeyStore зарезервирован (issue AID‑326) и сейчас бросает NotImplementedException. Для своих реализаций используйте Custom + AddCustomTokenStorage<TStorage>().

TokenRefreshOptions

Параметр Тип По умолчанию Описание
EnableAutoRefresh bool true Включить автообновление
RefreshCheckInterval TimeSpan 1 мин Период фоновой проверки
RefreshBeforeExpiration TimeSpan 5 мин За сколько до expiry начинать refresh
MaxRefreshAttempts int 3 Макс. попыток
RefreshRetryDelay TimeSpan 5 сек Задержка между попытками
RefreshOnFirstUse bool true Обновлять при первом использовании, если токен истекает

RetryOptions

Параметр Тип По умолчанию Описание
EnableRetry bool true Включить повторы
MaxRetryAttempts int 3 Кол‑во попыток
BaseDelay TimeSpan 1 сек Базовая задержка
MaxDelay TimeSpan 30 сек Максимум задержки
Strategy RetryStrategy ExponentialBackoff Fixed / Linear / ExponentialBackoff
BackoffMultiplier double 2.0 Множитель экспоненты
UseJitter bool true Случайный jitter
MaxJitterPercent int 20 Макс. % jitter (0..100)

Регистрация в DI

Доступные расширения IServiceCollection (AuthClient.Extensions.ServiceCollectionExtensions):

services.AddAuthClient(IConfiguration configuration);
services.AddAuthClient(IConfigurationSection section);
services.AddAuthClient(Action<AuthClientOptions> configure);
services.AddAuthClient(string serverUrl, string serviceId);

services.AddCustomTokenStorage<TStorage>();          // своя реализация ITokenStorage
services.ConfigureTokenStorage(opts => { ... });     // post-настройка
services.ConfigureTokenRefresh(opts => { ... });
services.ConfigureRetryPolicy(opts => { ... });

AddAuthClient(...) регистрирует:

  • IAuthenticationService, IUserRegistrationService, IRegistrationService, IProfileService, ITelegramService;
  • ITokenManagementService, ITokenValidationService, ITokenRefreshService, ITokenStorage;
  • IRoleService, ISystemRoleService, IOrganizationRoleService, IPermissionService, IUserRoleService;
  • IOrganizationService, IUserOrganizationService, IUserAssignmentService, IDepartmentService;
  • IPersonService, IPositionService, IEmployeeService, IWorkTeamService;
  • IServiceManagementService, IServiceUserAccessService, IServiceModuleService;
  • ICounterpartyService, ICounterpartyContactService;
  • IAccessLogService;
  • IServiceToServiceAuthenticationService + ServiceToServiceAuthHandler + CircuitBreakerAuthHandler;
  • AutoMapper с профилями библиотеки (валидация конфигурации в DEBUG через AutoMapperValidationService).

HttpClientFactory настраивается так, что CircuitBreakerAuthHandler и ServiceToServiceAuthHandler автоматически добавляются ко всем именованным/типизированным HttpClient. Запросы к самому AuthServer пропускают добавление S2S заголовков, чтобы избежать циклов.


Карта сервисов

Категория Интерфейс Назначение
Аутентификация IAuthenticationService Логин пользователей и сервисов, refresh, 2FA, S2S batch‑проверка
Валидация ITokenValidationService Runtime‑проверка JWT и разрешений
Регистрация заявок IRegistrationService Универсальная воронка регистрации (Telegram/Web/Mobile/API)
Пользователи IUserRegistrationService CRUD пользователей, активация, поиск
Профили IProfileService Полный профиль, Telegram аккаунты
Telegram ITelegramService TelegramUser CRUD/листинг
Токены ITokenManagementService Активные токены, отзыв, очистка, статистика
Старая модель ролей IRoleService Поддерживается для обратной совместимости
Системные роли ISystemRoleService Системные роли + разрешения + связь с сервисами
Орг. роли IOrganizationRoleService Роли уровня организации
Разрешения IPermissionService Реестр permission кодов и связь с ролями
Роли пользователей IUserRoleService Назначение ролей, поиск пользователей по роли
Организации IOrganizationService Иерархия, 2FA, пользователи организации
Связи user↔org IUserOrganizationService Принадлежность к организациям, primary org
Назначения IUserAssignmentService Назначения пользователей (должность/роль/служба)
Отделы IDepartmentService Иерархия отделов и их пользователи
Физлица IPersonService Person сущности и связь с User
Должности IPositionService Позиции/должности
Сотрудники IEmployeeService Employee = Person × Organization
Рабочие команды IWorkTeamService Иерархия рабочих команд
Сервисы IServiceManagementService Регистрация сервисов, кредентиалы, S2S разрешения
Доступы к сервисам IServiceUserAccessService Кому доступен сервис (через роли)
Модули сервисов IServiceModuleService Модули и их связи с сервисами
Контрагенты ICounterpartyService CRUD, поиск, юрисдикции
Контакты КА ICounterpartyContactService Контакты КА, миграция в Person
Логи доступа IAccessLogService Логи и статистика доступа
S2S IServiceToServiceAuthenticationService Получение/обновление сервисного токена, валидация входящих S2S

Все методы возвращают Task<TResult>, где TResult наследуется от BaseResult (Success, Message, ErrorCode, ErrorDetails). Конкретные результаты (AuthenticationResult, UserInfoResult, OrganizationResult, и т. д.) добавляют типизированные поля (Tokens, User, Organization, …).


Аутентификация и токены

IAuthenticationService — основной вход для пользователей и сервисов.

public interface IAuthenticationService
{
    Task<AuthenticationResult> LoginAsync(
        string username, string password, string serviceId,
        string? organizationId = null, CancellationToken cancellationToken = default);

    Task<AuthenticationResult> LoginTelegramAsync(
        long telegramId, string serviceId, string? password = null,
        string? organizationId = null, TelegramUserInfo? telegramUserInfo = null,
        CancellationToken cancellationToken = default);

    Task<AuthenticationResult> LoginByTelegramAndPhoneAsync(
        long telegramId, string phoneNumber, string serviceId,
        string? organizationId = null, CancellationToken cancellationToken = default);

    Task<AuthenticationResult> LoginServiceAsync(
        string apiKey, string apiSecret, CancellationToken cancellationToken = default);

    Task<AuthenticationResult> RefreshTokenAsync(string refreshToken, CancellationToken cancellationToken = default);
    Task<AuthenticationResult> RefreshServiceTokenAsync(string refreshToken, CancellationToken cancellationToken = default);

    Task<OperationResult> LogoutAsync(string accessToken, CancellationToken cancellationToken = default);

    Task<OperationResult> ChangePasswordAsync(
        string userId, string currentPassword, string newPassword, string serviceId,
        CancellationToken cancellationToken = default);

    Task<CompleteTwoFactorResult> CompleteTwoFactorAuthAsync(
        string twoFactorSessionId, string verificationCode,
        string? organizationId = null, CancellationToken cancellationToken = default);

    Task<ServiceAccessValidationResult> ValidateServiceAccessBatchAsync(
        string serviceId, List<string> permissionCodes, CancellationToken cancellationToken = default);
}

Пример контроллера:

using AuthClient.Models.Auth;
using AuthClient.Services.Interfaces;

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly IAuthenticationService _auth;

    public AuthController(IAuthenticationService auth) => _auth = auth;

    [HttpPost("login")]
    public async Task<IActionResult> Login(LoginRequestDto request, CancellationToken ct)
    {
        var result = await _auth.LoginAsync(
            request.Username,
            request.Password,
            serviceId: "my-service-id",
            organizationId: request.OrganizationId,
            cancellationToken: ct);

        if (!result.Success)
            return Unauthorized(new { error = result.Message, code = result.ErrorCode });

        if (result.Tokens.TwoFactorStatus == TwoFactorStatus.Required)
        {
            return Ok(new
            {
                require2Fa = true,
                twoFactorSessionId = result.Tokens.TwoFactorSessionId
            });
        }

        return Ok(new
        {
            accessToken  = result.Tokens.AccessToken,
            refreshToken = result.Tokens.RefreshToken,
            expiresAt    = result.Tokens.ExpiresAtDateTime,
            userId       = result.Tokens.UserId,
            serviceId    = result.Tokens.ServiceId,
            organizationId = result.Tokens.OrganizationId
        });
    }
}

AuthenticationResult.Tokens (AuthClient.Models.Auth.AuthTokens) содержит:

  • AccessToken, RefreshToken, ExpiresAt (Unix), ExpiresAtDateTime;
  • UserId, ServiceId, OrganizationId;
  • TwoFactorStatus?, TwoFactorSessionId?;
  • IsExpired, NeedsRefresh (за 5 минут до истечения).

Runtime‑проверка токенов и прав

ITokenValidationService используется в любом потребительском сервисе для проверок без обращения к БД напрямую:

public interface ITokenValidationService
{
    Task<TokenValidationResult> ValidateTokenAsync(
        string accessToken, string serviceId, CancellationToken cancellationToken = default);

    Task<PermissionCheckResult> CheckPermissionAsync(
        string userId, string serviceId, string permissionCode,
        string? organizationId = null, CancellationToken cancellationToken = default);

    Task<UserServicesResult> GetUserAvailableServicesAsync(
        string userId, CancellationToken cancellationToken = default);

    Task<TokenInfoResult> GetTokenInfoAsync(string tokenId, CancellationToken cancellationToken = default);
}

Глобальная проверка разрешения (без organizationId):

var check = await tokenValidation.CheckPermissionAsync(
    userId: userId,
    serviceId: "my-service-id",
    permissionCode: "users.read",
    cancellationToken: ct);

if (!check.HasPermission) return Forbid();

Org‑scoped проверка:

var check = await tokenValidation.CheckPermissionAsync(
    userId, "my-service-id", "users.read", organizationId: orgId, cancellationToken: ct);

IPermissionService — это админский сервис управления каталогом разрешений и связями ролей. Для runtime‑проверок типа «является ли пользователь админом» используйте ITokenValidationService.CheckPermissionAsync.

Управление токенами (админ)

ITokenManagementService — административные функции:

  • GetUserActiveTokensAsync(userId, page, pageSize, ct)
  • GetServiceActiveTokensAsync(serviceId, page, pageSize, ct)
  • RevokeTokenAsync(tokenId, reason, ct)
  • RevokeUserTokensAsync(userId, reason, excludeCurrent = false, ct)
  • RevokeServiceTokensAsync(serviceId, reason, ct)
  • CleanupExpiredTokensAsync(daysOld = 30, dryRun = false, ct)
  • GetTokenStatisticsAsync(startDate?, endDate?, includeDetails = false, ct)

Регистрация пользователей

Универсальная воронка заявок (IRegistrationService)

Поддерживает источники RegistrationSource: Telegram, Web, Mobile, Api.

var registration = await registrationService.SubmitRegistrationAsync(
    new RegistrationRequest
    {
        ServiceId = Guid.Parse("..."),
        TelegramId = 123456789,
        FirstName = "Иван",
        LastName = "Петров",
        PhoneNumber = "+79991234567",
        LanguageCode = "ru",
        Source = RegistrationSource.Telegram,
        UserMessage = "Хочу подключиться к боту"
    },
    serviceId: "my-bot-service-id",
    cancellationToken: ct);

if (!registration.Success)
    throw new InvalidOperationException(registration.Message);

Полный API:

  • SubmitRegistrationAsync(request, serviceId, ct)
  • GetRegistrationStatusAsync(telegramId, serviceId, ct)
  • CancelRegistrationAsync(telegramId, serviceId, ct)
  • ListRegistrationRequestsAsync(serviceId, status?, page, pageSize, ct) — права telegram.registration.view
  • ApproveRegistrationAsync(approveRequest, serviceId, ct) — права telegram.registration.approve
  • RejectRegistrationAsync(requestId, adminComment, serviceId, ct) — права telegram.registration.reject
  • VerifyEmailAsync(email, token, ct)
  • ResendEmailVerificationAsync(email, ct)

Для SubmitRegistrationAsync нужна предварительная авторизация сервиса через LoginServiceAsync(apiKey, apiSecret) — токен подставится автоматически из ITokenStorage.

CRUD пользователей (IUserRegistrationService)

  • RegisterUserAsync(username, email, password, ct)
  • GetUserAsync(userId, ct)
  • GetUserByUsernameAsync(username, ct)
  • UpdateUserAsync(userId, username?, email?, ct)
  • ActivateUserAsync(userId, ct) / DeactivateUserAsync(userId, ct)
  • ListUsersAsync(page, pageSize, search?, ct)

Профили (IProfileService)

  • GetProfileAsync(userId, ct)UserProfileResult с полным профилем (PersonProfile, Telegram аккаунты).
  • UpdateProfileAsync(userId, PersonProfile profile, ct)
  • AddTelegramAccountAsync(userId, TelegramAccount account, ct)
  • SetMainTelegramAccountAsync(userId, telegramAccountId, ct)

Управление ролями и разрешениями

Системные роли (ISystemRoleService)

CRUD + 2FA настройки, активация/деактивация, назначение разрешений и связь с сервисами: CreateSystemRoleAsync, GetSystemRoleAsync, GetSystemRoleByCodeAsync, UpdateSystemRoleAsync, DeleteSystemRoleAsync, ActivateSystemRoleAsync, DeactivateSystemRoleAsync, ListSystemRolesAsync, AssignPermissionToSystemRoleAsync, RemovePermissionFromSystemRoleAsync, GetSystemRolePermissionsAsync, AssignSystemRoleToServiceAsync, RemoveSystemRoleFromServiceAsync.

Орг. роли (IOrganizationRoleService)

CreateOrganizationRoleAsync, GetOrganizationRoleAsync, UpdateOrganizationRoleAsync, DeleteOrganizationRoleAsync, ActivateOrganizationRoleAsync, DeactivateOrganizationRoleAsync, ListOrganizationRolesAsync.

Разрешения (IPermissionService)

CRUD + связи с ролями: CreatePermissionAsync, GetPermissionAsync, UpdatePermissionAsync, DeletePermissionAsync, ListPermissionsAsync, AssignPermissionToRoleAsync, RemovePermissionFromRoleAsync, GetRolePermissionsAsync.

Назначение ролей пользователям (IUserRoleService)

  • AssignRoleToUserAsync(userId, systemRoleId, serviceId, organizationId?, ct)
  • RemoveRoleFromUserAsync(userId, systemRoleId, serviceId, organizationId?, ct)
  • GetUserRolesAsync(userId, serviceId?, organizationId?, ct)
  • GetUsersByRoleAsync(systemRoleId, page, pageSize, organizationId?, includeInactive, ct)
  • GetUsersByServiceRoleAsync(systemRoleId, serviceId, page, pageSize, organizationId?, includeInactive, ct)

Старый IRoleService

Сохранён для обратной совместимости (плоская модель ролей сервиса). Для нового кода предпочитайте ISystemRoleService / IOrganizationRoleService.


Управление организационной структурой

Организации (IOrganizationService)

  • CreateOrganizationAsync(code, name, description, parentOrganizationId?, twoFactorEnabled, twoFactorType, ct)
  • GetOrganizationAsync(organizationId, ct)
  • UpdateOrganizationAsync(organizationId, code, name, description, twoFactorEnabled?, twoFactorType?, ct)
  • ActivateOrganizationAsync / DeactivateOrganizationAsync / DeleteOrganizationAsync(forceDelete = false)
  • ListOrganizationsAsync(page, pageSize, parentOrganizationId?, includeInactive, ct)
  • GetChildOrganizationsAsync(parentOrganizationId, includeAllLevels, ct)
  • SetParentOrganizationAsync(organizationId, parentOrganizationId?, ct)
  • GetOrganizationUsersAsync(organizationId, page, pageSize, includeChildOrganizations, ct)

Связи user ↔ org (IUserOrganizationService)

AddUserToOrganizationAsync, RemoveUserFromOrganizationAsync, GetUserOrganizationsAsync, SetUserPrimaryOrganizationAsync, GetUserServiceAccessAsync(userId, serviceId?, organizationId?, ct).

Назначения (IUserAssignmentService)

Управление назначениями пользователей (должность/роль/служба): CreateUserAssignmentAsync, UpdateUserAssignmentAsync, ArchiveUserAssignmentAsync(reason), DeactivateUserAssignmentAsync, GetUserAssignmentsAsync, GetOrganizationAssignmentsAsync, SetPrimaryAssignmentAsync. Тип задаётся через UserAssignmentType (OrganizationPosition по умолчанию).

Отделы (IDepartmentService)

CRUD + иерархия + пользователи: CreateDepartmentAsync, GetDepartmentAsync, UpdateDepartmentAsync, ActivateDepartmentAsync, DeactivateDepartmentAsync, DeleteDepartmentAsync(forceDelete), ListDepartmentsAsync(organizationId, …), GetChildDepartmentsAsync, GetRootDepartmentsAsync, GetDepartmentHierarchyAsync, GetDepartmentPathAsync, GetDepartmentUsersAsync.

Физические лица (IPersonService)

CreatePersonAsync, GetPersonAsync, GetPersonByUserIdAsync, UpdatePersonAsync, DeletePersonAsync, ListPersonsAsync(page, pageSize, searchText?, ct), SearchPersonsAsync(searchTerm, ct). Поддерживаются Gender, PersonType (Human по умолчанию).

Должности (IPositionService)

CreatePositionAsync, GetPositionAsync, GetPositionByCodeAsync, UpdatePositionAsync, DeletePositionAsync, ListPositionsAsync, GetPositionsByCategoryAsync, GetPositionsByLevelAsync, GetEmployeeCountAsync.

Сотрудники (IEmployeeService)

CreateEmployeeAsync(personId, organizationId, departmentId?, positionId?, hireDate?, employeeNumber?, notes?, ct), GetEmployeeAsync, UpdateEmployeeAsync, TerminateEmployeeAsync(employeeId, reason, terminationDate?, ct), DeleteEmployeeAsync, ListEmployeesAsync, GetEmployeesByOrganizationAsync, GetEmployeesByDepartmentAsync. Поддерживается EmployeeStatus.

Рабочие команды (IWorkTeamService)

CreateWorkTeamAsync, GetWorkTeamAsync, GetWorkTeamByCodeAsync, UpdateWorkTeamAsync, DeleteWorkTeamAsync, ListWorkTeamsAsync, GetWorkTeamsByOrganizationAsync, GetChildWorkTeamsAsync(recursive), GetWorkTeamHierarchyAsync(maxDepth).


Управление сервисами и модулями

Регистрация сервисов (IServiceManagementService)

  • CRUD сервисов: RegisterServiceAsync, GetServiceAsync, UpdateServiceAsync, ActivateServiceAsync, DeactivateServiceAsync, ListServicesAsync.
  • Кредентиалы: RegenerateCredentialsAsync(serviceId)ServiceCredentialsResult (новые ApiKey/ApiSecret).
  • Разрешения сервиса: AssignPermissionToServiceAsync, RemovePermissionFromServiceAsync, GetServicePermissionsAsync.
  • Статистика: GetServiceUsageStatsAsync(serviceId, startDate?, endDate?, ct).
  • Service-to-Service permissions: GrantServiceToServicePermissionAsync(sourceServiceId, targetServiceId, permissionCode, description?, expiryDate?, ct), RevokeServiceToServicePermissionAsync(permissionId), ListServiceToServicePermissionsAsync(sourceServiceId?, targetServiceId?, page, pageSize).
  • Доступ пользователей: GetServiceAccessibleUsersAsync, CheckUserAccessToServiceAsync, GetUserServicesAsync.

Доступы (IServiceUserAccessService)

Расширенный API для интеграций: GetServiceAccessibleUsersAsync(serviceId, page, pageSize, systemRoleId?, organizationId?, includeInactive, ct), CheckUserAccessToServiceAsync(userId, serviceId, organizationId?, ct), GetUserServicesAsync(userId, organizationId?, ct).

Модули сервисов (IServiceModuleService)

CRUD модулей и связей модуль ⇄ сервис: CreateServiceModuleAsync, GetServiceModuleAsync, UpdateServiceModuleAsync, DeleteServiceModuleAsync, ActivateServiceModuleAsync, DeactivateServiceModuleAsync, ListServiceModulesAsync, GetServiceModulesByServiceAsync(serviceId, activeOnly), LinkModuleToServiceAsync, UnlinkModuleFromServiceAsync, UpdateServiceModuleLinkAsync, GetServiceModuleLinkAsync, ListServiceModuleLinksAsync.


Контрагенты

Контрагенты (ICounterpartyService)

  • CRUD: CreateCounterpartyAsync, GetCounterpartyAsync(id, includeContacts), UpdateCounterpartyAsync, ActivateCounterpartyAsync, DeactivateCounterpartyAsync, DeleteCounterpartyAsync.
  • Поиск: ListCounterpartiesAsync(page, pageSize, activeOnly, jurisdiction?, countryCode?, ct), SearchCounterpartiesAsync(searchTerm, …), GetCounterpartiesByJurisdictionAsync(jurisdiction, …).
  • Юрисдикционные данные: GetCounterpartyJurisdictionDataAsync, UpdateCounterpartyJurisdictionDataAsync(taxId, registrationNumber, vatNumber, legalForm, regulatoryAuthority, licenseNumber, registrationDate?, additionalData?, ct).

Контакты КА (ICounterpartyContactService)

CRUD + поиски + миграция в Person: CreateCounterpartyContactAsync, GetCounterpartyContactAsync, UpdateCounterpartyContactAsync, DeleteCounterpartyContactAsync, ListCounterpartyContactsAsync, GetByCounterpartyIdAsync, GetByOrganizationIdAsync, GetByDepartmentIdAsync, GetPrimaryContactAsync, MarkAsOutdatedAsync(contactId, markedByOrganizationId, reason, ct), MigrateToPersonAsync(contactId, personId, ct), GetMigrateableContactsAsync, SetPrimaryContactAsync.


Логирование и аналитика

IAccessLogService — доступ к логам и агрегатам:

  • GetAccessLogsAsync(page, pageSize, startDate?, endDate?, errorsOnly?, serviceId?, userId?, ct)
  • GetAccessLogsByUserAsync(userId, …), GetAccessLogsByServiceAsync(serviceId, …)
  • GetAccessStatisticsAsync(startDate?, endDate?, serviceId?, userId?, includeHourly, includeDaily, ct)
  • GetTopUsersByActivityAsync(startDate?, endDate?, limit, ct)
  • GetTopServicesByUsageAsync(startDate?, endDate?, limit, ct)
  • GetErrorStatisticsAsync(startDate?, endDate?, serviceId?, topLimit, ct)
  • CleanupOldLogsAsync(daysToKeep = 90, dryRun = false, ct)

Telegram интеграция

ITelegramService работает только с сущностью TelegramUser (не путать с пользователями системы):

  • GetTelegramUserAsync(telegramId, serviceId, ct)
  • UpdateTelegramUserAsync(UpdateTelegramUserRequest request, serviceId, ct)
  • DeactivateTelegramUserAsync(userId, ct)
  • ListTelegramUsersAsync(isActive?, page, pageSize, ct)

Логин по Telegram реализован в IAuthenticationService.LoginTelegramAsync / LoginByTelegramAndPhoneAsync. Регистрация Telegram‑пользователей — через IRegistrationService.


Middleware для ASP.NET Core

Библиотека предоставляет три middleware (через AuthClient.Extensions и AuthClient.Middleware):

Метод Назначение
app.UseAuthClient() (AuthClient.Extensions) Подключает ServiceToServiceAuthMiddleware для прозрачной валидации входящих S2S запросов (заголовки X-Service-Token, X-Request-Id). Рекомендован к использованию во всех ASP.NET Core сервисах AIDeskLab.
app.UseAuthClient() (AuthClient.Middleware.AuthClientMiddlewareExtensions) Подключает AuthClientMiddleware — автоматически достаёт токен из ITokenStorage, обновляет при необходимости, кладёт в HttpContext.Items["AccessToken"]. Подходит для сервисов‑клиентов, которые сами вызывают другие API от имени пользователя.
app.UseTokenValidation() Подключает TokenValidationMiddleware для валидации JWT по TokenValidationOptions (issuer/audience/lifetime/signing key, cookie, query string).

Для большинства новых сервисов AIDeskLab достаточно вызвать UseAuthClient() из пространства имён AuthClient.Extensions. Если нужно дополнительно автоматически инъектировать токены — подключите второй UseAuthClient() (через namespace AuthClient.Middleware) после первого.

HttpContext обогащается полезными элементами (UserId, OrganizationId, UserRoles, UserPermissions, AccessToken, CallingServiceId, CallingServiceName, CallingServicePermissions, InterServiceRequestId). Для удобства используйте HttpContextExtensions:

using AuthClient.Extensions;

string? userId = HttpContext.GetUserId();
string? orgId  = HttpContext.GetOrganizationId();
bool isAuthn   = HttpContext.IsAuthenticated();
bool canEdit   = HttpContext.UserHasPermission("posts.edit");

В non‑web сценариях middleware не используются — работайте напрямую с DI.


Межсервисная авторизация (Service-to-Service)

Это ключевая «прозрачная» возможность библиотеки. После AddAuthClient(...):

  1. Ко всем HttpClient в DI автоматически добавляются CircuitBreakerAuthHandler и ServiceToServiceAuthHandler. Любой исходящий HTTP‑вызов получает заголовки X-Service-Token и X-Request-Id.
  2. Запросы к самому AuthServer заголовки не получают (защита от циклов).
  3. На принимающей стороне ServiceToServiceAuthMiddleware проверяет токен и права через IServiceToServiceAuthenticationService.ValidateAccessFromTokenAsync с локальным MemoryCache (TTL = LocalCacheExpiry, лимит = MaxLocalCacheSize).

IServiceToServiceAuthenticationService:

public interface IServiceToServiceAuthenticationService
{
    Task<string> GetServiceTokenAsync(CancellationToken cancellationToken = default);

    Task<ServiceAccessValidationResult> ValidateAccessFromTokenAsync(
        string callingServiceToken,
        string targetServiceId,
        string requestId,
        CancellationToken cancellationToken = default);

    bool IsTokenExpiringSoon(string token, TimeSpan threshold);

    Task<string> RefreshServiceTokenNowAsync(CancellationToken cancellationToken = default);
}

Чек‑лист включения:

  • В appsettings.json укажите ServiceId, ApiKey, ApiSecret, EnableServiceToServiceAuth=true.
  • В сервисе‑потребителе вызывайте обычные HttpClient — заголовки добавятся сами.
  • На принимающей стороне в Program.cs вызовите app.UseAuthClient() до UseAuthorization().
  • Для пакетной проверки прав (например, в API‑Gateway) используйте IAuthenticationService.ValidateServiceAccessBatchAsync(serviceId, permissionCodes, ct).

Если требуется отключить S2S заголовки в одном сервисе — установите EnableServiceToServiceAuth = false.


Хранение токенов

Интерфейс ITokenStorage (AuthClient.TokenManagement):

public interface ITokenStorage
{
    Task SaveTokensAsync(AuthTokens tokens, CancellationToken ct = default);
    Task<AuthTokens?> GetTokensAsync(CancellationToken ct = default);
    Task UpdateTokensAsync(AuthTokens tokens, CancellationToken ct = default);
    Task ClearTokensAsync(CancellationToken ct = default);
    Task<bool> HasValidTokensAsync(CancellationToken ct = default);
    Task<string?> GetAccessTokenAsync(CancellationToken ct = default);
    Task<string?> GetRefreshTokenAsync(CancellationToken ct = default);
}

Готовые реализации:

  • InMemoryTokenStorage — самый простой вариант для веб‑приложений за reverse proxy.
  • FileTokenStorage — для CLI/desktop. Требует FilePath и (рекомендуется) шифрование.
  • SystemKeyStoreTokenStorage — заглушка, бросает NotImplementedException (issue AID‑326).

Подключение собственной реализации:

services.AddAuthClient(configuration);
services.AddCustomTokenStorage<MyRedisTokenStorage>();

При EncryptTokens=true обязательно задайте EncryptionKey (≥ 32 символа). Шифрование выполняется через CryptographyHelper.

ITokenRefreshService запускается как singleton background компонент и согласно TokenRefreshOptions сам обновляет токены до их истечения; внутри библиотеки используется ForceRefreshAsync() для немедленного refresh.


Обработка ошибок

Все методы возвращают объект, наследующий BaseResult. Стандартный паттерн:

var result = await _auth.LoginAsync(username, password, "my-service-id", cancellationToken: ct);

if (!result.Success)
{
    _logger.LogWarning(
        "Login failed: code={Code}, message={Message}, details={Details}",
        result.ErrorCode, result.Message, result.ErrorDetails);
    return Unauthorized(new { code = result.ErrorCode, message = result.Message });
}

Дополнительно библиотека определяет типизированные исключения (AuthClient.Exceptions):

  • AuthClientException — базовое;
  • AuthenticationException — ошибки аутентификации;
  • AuthorizationException — ошибки авторизации;
  • ConfigurationException — невалидные настройки;
  • ServerCommunicationException — сетевые/gRPC сбои;
  • TokenStorageException — проблемы хранилища.

Они могут быть проброшены из инфраструктурного кода (например, при сбое gRPC). В прикладном коде обычно достаточно проверки result.Success.


Best Practices

  1. Один ServiceId на сервис — он используется и в S2S‑заголовках, и в runtime‑проверках. Не путайте с ServiceCode.
  2. Секреты — в Secret Manager: ApiKey, ApiSecret, EncryptionKey не должны попадать в git.
  3. Для пользовательских правITokenValidationService.CheckPermissionAsync. IPermissionService нужен только администраторам каталога разрешений.
  4. Для проверок по организации всегда передавайте organizationId, иначе проверка будет глобальной по сервису.
  5. Web API — обязательно вызывайте app.UseAuthClient() (S2S middleware) до UseAuthorization().
  6. Не используйте app.UseAuthClient() в Generic Host — middleware не для non‑web. Сервисы получайте через DI.
  7. Refresh — не выключайте автообновление в production. Если делаете отдельный RefreshTokenAsync, не забывайте ITokenStorage.UpdateTokensAsync.
  8. Локальный кэш S2S — настраивайте LocalCacheExpiry/MaxLocalCacheSize под нагрузку. По умолчанию 15 минут / 10 000 записей.
  9. Retry политики — для нагруженных операций ставьте Strategy=ExponentialBackoff, UseJitter=true. Не задирайте MaxRetryAttempts выше 5.
  10. AutoMapper в DEBUG — при сборке в Debug запускается AutoMapperValidationService, который проверяет конфигурацию профилей при старте. Не отключайте — это ловит несовместимости proto и моделей.

Совместимость и версионирование

  • Целевой framework: net8.0.
  • gRPC сервер: AIDesk.AuthServer.Contracts (версия фиксируется в csproj).
  • Версия пакета формируется через Nerdbank.GitVersioning (version.json). Семантика — SemVer:
    • MAJOR — breaking changes публичного API;
    • MINOR — обратно‑совместимая функциональность;
    • PATCH — багфиксы.
  • Поддерживаются Windows, Linux, macOS (определены символы WINDOWS, LINUX, MACOS).

История изменений ведётся в репозитории AuthServer (docs/projects/AuthClient/CHANGELOG.md). Подробные прикладные сценарии — в docs/projects/AuthClient/USAGE_EXAMPLES.md.


Лицензия и ссылки

No packages depend on AIDeskLab.AuthClient.

Version Downloads Last updated
1.1.7 1 05/14/2026
1.1.4 5 04/29/2026
1.1.3-g1b2b06ce20 3 04/28/2026
1.0.11-gbcaac7719d 9 01/16/2026
1.0.10-g8e6a734f1e 6 01/16/2026
1.0.7-g470a3ef930 6 01/16/2026