Microservice: Базовый авторизационный сервис, токен доступа, токен обновления

Спецификация gRPC-интерфейсов, логики авторизации и архитектуры хранения данных

Published

June 11, 2026

WarningОграничение публичной документации

В открытом доступе представлена демонстрационная версия метода. В настоящей публичной документации отображены не все шаги, технические сценарии и приватные эндпоинты для системы цифровых симуляторов бизнес-процессов.

  • Полная спецификация метода: Будет доступна только во внутреннем контуре разработки (Confluence / Swagger Enterprise).

1 Начало

Создадим базовый авторизационный сервис пользователей (реальных и “цифровых”) для полученния доступа к эндпоинтам приложения. Методы сервиса будут синхронными и будут предоставлять возможность: регистрации, авторизации и выхода из приложения. И дополнительные методы ваоидации и обновления токена.

1.1 1. Общее назначение и архитектурная роль сервиса

Микросервис Auth-Service выполняет функции единого центра аутентификации, ведения активных сессий и авторизации межсервисных запросов в системе. Компонент реализован как высокопроизводительный gRPC-сервер на языке Go.

1.1.1 Ключевые архитектурные решения:

  • Оптимизация межсервисного взаимодействия: Идентификатор группы пользователя (group_id), к которой он привязан, извлекается при авторизации и вшивается в криптографическое клеймо MapClaims JWT-токена подписи HMAC-SHA256. Это исключает необходимость для смежных доменных воркеров выполнять повторные SQL-запросы в базу авторизации — они получают контекст группы напрямую из валидированной структуры токена.
  • Сквозная трассировка (Distributed Tracing): Транспортный слой оснащен перехватчиком UnaryServerInterceptor, который автоматически проверяет наличие заголовка x-request-id в gRPC-метаданных. Если клиент или прокси-сервер не передали маркер, интерцептор генерирует UUID v4, регистрирует его в контексте и обогащает им каждую строку структурированного JSON-лога в os.Stdout.

1.2 2. Структура и конфигурация слоя баз данных (Реестр СУБД)

Компонент инициализации инфраструктуры DB обеспечивает параллельное подключение к реляционному пулу PostgreSQL и кэш-слою Redis с обязательным синхронным вызовом процедуры Ping() при старте.

1.2.1 2.1. Конфигурация пула соединений PostgreSQL (pgx/v5)

Для минимизации накладных расходов на сетевые транзакции используется пул соединений драйвера pgxpool. Настройки пула зафиксированы в коде: * MaxConns — Максимальный лимит одновременно открытых сессий пула равен 25. * MinConns — Минимальное количество гарантированно удерживаемых коннектов равно 5. * MaxConnLifetime — Предельный жизненный цикл сетевого соединения составляет 30 минут, после чего оно мягко переоткрывается.

1.2.2 2.2. Конфигурация сессионного кэша Redis (go-redis/v9)

Репозиторий SessionRepository изолирует хранение временных долгоживущих токенов в оперативной памяти Redis на логическом секторе DB 0. Ключи формируются по маске refresh:UUID_ТОКЕНА со строгим ограничением времени жизни (TTL), равным 30 дням (30 * 24 * Hour). При выходе из системы или ротации ключей запись атомарно уничтожается методом DeleteSession.

1.2.3 2.3. ER-диаграмма базы данных (Связи сущностей)

%%{init: {
  'theme': 'base', 
  'er': {
    'useMaxWidth': false
  },
  'themeVariables': {
    'mainBkg': '#FFF9C4',
    'lineColor': '#2C3E50',
    'borderClassName': '#FBC02D',
    'nodeBorder': '#FBC02D',
    'attributeBackend': '#FFF9C4'
  }
}}%%

erDiagram
    users_postgres {
        int64 id PK
        varchar email UK
        varchar password_hash
        varchar user_type
        uuid group_id
        boolean is_active
        timestamp created_at
        timestamp updated_at
    }

    sessions_redis {
        string key_token_uuid PK
        int64 user_id
    }

    users_postgres ||--o{ sessions_redis : "has active"
    


1.3 3. Спецификация gRPC-интерфейсов (Транспортный контракт)

Сервер обрабатывает данные в рамках спецификации AuthServiceServer, преобразуя внутренние исключения репозиториев в канонические коды ошибок google.golang.org/grpc/codes.

1.3.1 3.1. Метод ValidateToken (Авторизация токена доступа)

Вызывается шлюзами интеграции для подтверждения прав клиента.

  • Входные параметры (pb.ValidateRequest): Строковое поле access_token. При пустом значении сервер возвращает статус codes.InvalidArgument.
  • Выходные параметры (pb.ValidateResponse): Логический флаг is_valid, числовой идентификатор пользователя user_id, строковые метки типа аккаунта user_type (Строго фиксированные доменные константы: REAL или DIGITAL) и UUID-строка group_id. При невалидной криптографической подписи или истечении срока действия токена отдается статус codes.Unauthenticated.

1.4 4. Сценарий взаимодействия при валидации сессии (Sequence Diagram)

Ниже представлена диаграмма обработки запроса верификации токена, разбора метаданных трассировки и логического маппинга параметров группы.

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'actorBkg': '#E3F2FD',
    'actorBorder': '#546E7A',
    'actorTextColor': '#0D47A1',
    'rectBkg': '#FFF9C4',
    'rectBorder': '#FBC02D',
    'noteBkgColor': '#F3E5F5',
    'noteBorderColor': '#7E57C2',
    'noteTextColor': '#311B92',
    'signalColor': '#2C3E50',
    'signalLineColor': '#2C3E50'
  }
}}%%
sequenceDiagram
    autonumber
    actor Client as Внешний gRPC Компонент
    participant Interceptor as LoggingInterceptor (Go)
    participant Server as GRPCServer Transport
    participant UR as UserRepository (Postgres)

    Client->>Interceptor: Вызов метода gRPC ValidateToken с MD [x-request-id]
    activate Interceptor
    
    note over Interceptor: Шаг 2: Извлечение x-request-id из метаданных.<br/>При отсутствии — генерация uuid.New().String()
    note over Interceptor: Шаг 3: Инициализация JSON-логгера slog с фиксацией request_id
    
    Interceptor->>Server: Перенаправление запроса во внутренний транспортный хэндлер
    activate Server
    
    Server->>UR: Вызов репозитория пользователей по ID из токена GetByID(id)
    activate UR
    UR-->>Server: Возврат User (ID, UserType, IsActive, GroupID)
    deactivate UR

    note over Server: Шаг 7: Проверка активности IsActive и маппинг GroupID в наружное поле GroupId
    
    Server-->>Interceptor: Проброс protobuf-структуры ValidateResponse и статуса ошибки
    deactivate Server
    
    note over Interceptor: Шаг 9: Расчет duration_ms и запись лога JSON Lines в os.Stdout
    
    Interceptor-->>Client: Ответ ValidateResponse + gRPC Status Code
    deactivate Interceptor

1.4.1 4.1. Таблица расшифровки шагов сценария ValidateToken

Шаг Действие Параметры / Запросы / DTO Код ошибки (canonical_code) HTTP / gRPC статус
1 (Client -> Interceptor) Смежный микросервис выполняет вызов метода проверки сессии, передавая маркер трассировки в метаданных gRPC. gRPC Metadata Context:
x-request-id: "uuid-v4-trace-id"
AUTH_INVALID_ARGUMENT codes.InvalidArgument (400)
2 (Interceptor -> Interceptor) Внутренняя логика: Интерцептор извлекает маркер из контекста. При его отсутствии генерирует новый UUID и регистрирует его в контексте под ключом RequestIDKey. Внутренняя функция:
getOrCreateRequestID(ctx)
Нет codes.OK (200)
3 (Interceptor -> Interceptor) Внутренняя логика: В поток os.Stdout сбрасывается JSON-лог о начале обработки запроса с привязанным идентификатором трассировки. slog JSON Line Payload:
{ "msg": "gRPC request started", "request_id": "uuid", "method": "/pb.AuthService/ValidateToken" }
Нет codes.OK (200)
4 (Interceptor -> Server) Интерцептор передает управление на транспортный слой gRPC-сервера, пробрасывая модифицированный контекст. Внутренний вызов Go:
handler(ctx, req)
Нет codes.OK (200)
5 (Server -> UR) Сервис проверяет подпись входящего токена через jwt.Parse и выполняет запрос к репозиторию для извлечения актуальной карточки по ID. Внутренний метод репозитория:
r.pool.QueryRow(ctx, query, id).Scan(...)
DB_CONNECTION_FAILURE codes.Internal (500)
6 (UR -> Server) Репозиторий перехватывает ошибку отсутствия строк pgx.ErrNoRows, трансформирует ее в доменное исключение и возвращает данные. SQL Response:
Кортеж полей (id, email, password_hash, user_type, is_active, group_id)
AUTH_USER_NOT_FOUND codes.Unauthenticated (401)
7 (Server -> Server) Внутренняя логика: Транспортный слой проверяет флаг блокировки аккаунта user.IsActive и осуществляет финальный маппинг данных в структуру ответа. Protobuf Response Message:
pb.ValidateResponse{GroupId: groupID, IsValid: true}
AUTH_ACCOUNT_SUSPENDED codes.Unauthenticated (401)
8 (Server -> Interceptor) gRPC-сервер возвращает сформированный protobuf-ответ обратно в слой интерцептора логирования. gRPC Internal Response Нет codes.OK (200)
9 (Interceptor -> Interceptor) Внутренняя логика: Фиксация общего времени обработки запроса. Запись итогового лога в JSON-формате в stdout. slog JSON Line Payload:
{ "level": "INFO", "duration_ms": 11.4, "request_id": "trace-id" }
CRYPTO_SIGNING_ERROR codes.Internal (500)
10 (Interceptor -> Client) Финальный ответ со статусом проверки и идентификатором группы группы уходит вызывающей доменной системе. gRPC Wire Response DTO:
ValidateResponse{is_valid: true, group_id: "uuid"}
Нет codes.OK (200)

1.5 5. Справочник Обсервабилити (Структура логов slog для ClickHouse)

Каждое событие, проходящее через интерцептор, пишется в os.Stdout в виде плоской JSON-строки. При обработке ошибок Vector отправляет в ClickHouse следующий набор канонических полей:

1. Код (canonical_code) 2. Уровень (log_level) 3. Статусы (transport_statuses) 4. Получатель (error_target) 5. JSON для Фронтенда (ui_payload) 6. Метрики для ClickHouse (observability_json)
AUTH_INVALID_ARGUMENT WARN {“grpc”: 3, “http”: 400} EXTERNAL_SYSTEM null {“metric”: “validation_fail”, “labels”: {“reason”: “missing_token_or_email”}}
AUTH_TOKEN_EXPIRED WARN {“grpc”: 16, “http”: 401} EXTERNAL_SYSTEM null {“metric”: “auth_fail”, “labels”: {“reason”: “invalid_token_signature_or_expired”}}
AUTH_ACCOUNT_SUSPENDED WARN {“grpc”: 7, “http”: 403} EXTERNAL_SYSTEM null {“metric”: “login_blocked”, “labels”: {“reason”: “user_is_blocked”}}
DB_CONNECTION_FAILURE ERROR {“grpc”: 13, “http”: 500} INTERNAL_SYSTEM null {“metric”: “db_pool_error”, “labels”: {“service”: “auth_service”}}