Авторизация в C# через клиентскую библиотеку
В этом разделе подробно описан полный цикл авторизации в web-API ПОЛИНОМ:MDM средствами
клиентской библиотеки Ascon.Polynom.Web.Api.Client: как устроена авторизация и как ею
пользоваться. Пример, демонстрирующий все шаги, — Samples.WebApi.Authorization.
Авторизация реализуется классом
AuthorizationService, а HTTP-вызовы
сервисов API автоматически дополняются заголовком Authorization: Bearer делегирующим обработчиком
AuthorizationMessageHandler.
Писать ручные запросы и подставлять токен вручную не требуется.
Как устроена авторизация
- Клиентское приложение указывает адрес web-сервера и параметры в
PolynomApiClientOptions. - Приложение получает список хранилищ и выполняет вход с логином, паролем и идентификатором хранилища.
- Web-сервер возвращает пару токенов: токен доступа (
access_token) и токен обновления (refresh_token). - Токен доступа действует 10 минут. Библиотека хранит его, подставляет в запросы и умеет
обновлять:
- по таймеру — за
RefreshTokenBeforeExpiryдо истечения; - по ответу
401 Unauthorized— однократное обновление и повтор запроса.
- по таймеру — за
- При выходе токены удаляются, а сессия закрывается на web-сервере (при настройке — и в общем сервисе авторизации, ОСА).
Важно. Срок действия токена доступа ограничен. Полагайтесь на автоматическое продление библиотеки, а не на ручное управление токеном.
Компоненты библиотеки
| Компонент | Назначение |
|---|---|
PolynomApiClientFactory |
Фабрика клиентов для работы без DI; владеет сервисом авторизации и HTTP-обработчиком |
PolynomApiClientOptions |
Параметры клиента: адрес, режим продления, хранение токенов |
AuthorizationService |
Вход, выход, обновление и автопродление токена |
AuthorizationMessageHandler |
Подставляет заголовок Authorization и повторяет запрос при 401 |
ITokenStorage, AuthTokens |
Хранение снимка токенов |
ITokenProtector, TokenProtectorFactory |
Шифрование токенов (в памяти или на диске) |
ILoginApiClientFactory |
Предоставляет LoginApiClient на время операции |
Регистрация клиента
Режим без DI (фабрика)
Для консольных приложений используется
PolynomApiClientFactory:
using Ascon.Polynom.Web.Api.Client;
using Ascon.Polynom.Web.Api.Client.Auth;
// Протектор токенов на базе Data Protection; ключи размещаются в каталоге.
var protector = TokenProtectorFactory.CreateDataProtection(
Path.Combine(Path.GetTempPath(), "PolynomApiClientKeys"));
using var factory = new PolynomApiClientFactory(
new PolynomApiClientOptions
{
BaseAddress = new Uri("http://localhost:5100"),
PersistTokens = false,
ProtectTokensInMemory = true,
RefreshOnUnauthorized = true
},
protector);
Режим с DI
В приложениях ASP.NET Core клиенты регистрируются расширением AddPolynomApiClient:
builder.Services.AddPolynomApiClient(options =>
{
options.BaseAddress = new Uri("https://polynom-mdm.local");
options.SsoUrl = "https://sso.local";
options.PersistTokens = true;
options.TokenStoragePath = Path.Combine(AppContext.BaseDirectory, "tokens.protected");
});
Расширение регистрирует AuthorizationService,
хранилище и протектор токенов, именованный HTTP-клиент с
AuthorizationMessageHandler
и все сервисы *ApiClient.
Параметры клиента
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
BaseAddress |
Uri? |
— | Базовый адрес web-API. Обязателен |
SsoUrl |
string? |
— | Адрес сервера ОСА для выхода из единой сессии |
RefreshTokenBeforeExpiry |
TimeSpan |
2 минуты | За сколько до истечения продлевать токен |
AutoRefreshToken |
bool |
true |
Автопродление токена по таймеру |
RefreshOnUnauthorized |
bool |
true |
Обновлять токен и повторять запрос при 401 |
ProtectTokensInMemory |
bool |
true |
Шифрование токенов в памяти |
PersistTokens |
bool |
false |
Сохранять токены между запусками |
TokenStoragePath |
string? |
— | Путь к файлу токенов (обязателен при PersistTokens) |
Полный цикл авторизации
Ниже — сценарий, повторяющий пример Samples.WebApi.Authorization: список хранилищ → вход → обновление токена → выход. Метод возвращает признак успешной авторизации, а не печатает состояние.
using Ascon.Polynom.Web.Api.Client.Auth;
using Ascon.Polynom.Web.Api.Client.Enums;
using Ascon.Polynom.Web.Api.Client.Interfaces;
using Ascon.Polynom.Web.Api.Client.Models;
// Шаг 1. Список хранилищ (без авторизации) и выбор нужного.
var storages = await factory.CreateLoginApiClient().StorageDefinitions()
?? new List<IStorageDefinition>();
var storage = storages.FirstOrDefault(s => s.DisplayName == "Test")
?? throw new InvalidOperationException("Хранилище «Test» не найдено.");
// Шаг 2. Вход по логину и паролю.
var authResponse = await factory.Authorization.SignInAsync(new SignInRequest
{
Login = "admin",
Password = password,
StorageId = storage.StorageId.ToString(),
ClientType = ClientTypes.Client
});
if (string.IsNullOrEmpty(authResponse?.Access_token))
throw new InvalidOperationException("Web-сервер не вернул токен доступа.");
// Шаг 3. Проверка состояния авторизации.
if (!factory.Authorization.IsAuthenticated)
throw new InvalidOperationException("Авторизация не выполнена.");
// Шаг 4. Продление токена по токену обновления.
var refreshed = await factory.Authorization.UpdateTokenAsync();
if (!refreshed)
throw new InvalidOperationException("Не удалось продлить токен доступа.");
// Шаг 5. Выход из сессии.
await factory.Authorization.SignOutAsync();
SignInAsync принимает
SignInRequest и возвращает
IAuthResponse с полями Access_token,
Refresh_token и Expires_in. Перечисление ClientTypes
задаёт тип клиента (Client, Importer, Editor, Administrator).
Вход по готовому токену доступа (в том числе ОСА)
Если токен доступа уже получен (например, из общего сервиса авторизации), вход выполняется без пароля:
var authResponse = await factory.Authorization.SignInByAccessTokenAsync(
accessToken,
new SignInRequest
{
Login = "user",
StorageId = storage.StorageId.ToString(),
ClientType = ClientTypes.Client
},
tokenFromSso: true);
Признак tokenFromSso: true запоминает токен ОСА, чтобы при выходе закрыть и сессию единой
авторизации. Адрес ОСА задаётся параметром
SsoUrl или методом
SetSsoUrl. Настройка ОСА на стороне web-сервера описана в разделе
Авторизация с помощью общего сервиса авторизации.
Обновление и автопродление токена
AutoRefreshToken = trueзапускает фоновый таймер: библиотека сама обновляет токен заRefreshTokenBeforeExpiryдо истечения.RefreshOnUnauthorized = trueвключает реакцию на ответ401: однократное обновление токена и повтор исходного запроса. Повтор не выполняется для эндпоинтов авторизации.- Ручное обновление доступно методом
UpdateTokenAsync:
var updated = await factory.Authorization.UpdateTokenAsync();
if (!updated)
throw new InvalidOperationException("Токен обновления отсутствует или отклонён сервером.");
Выход из сессии
await factory.Authorization.SignOutAsync();
Метод закрывает сессию web-API и, если настроен ОСА и вход был по токену ОСА, — сессию единой
авторизации. Только выход из ОСА выполняет SignOutSsoAsync.
Хранение токенов
| Режим | Реализация | Когда использовать |
|---|---|---|
| В памяти | InMemoryTokenStorage |
Кратковременные сценарии; токены не сохраняются на диск |
| На диске | FileTokenStorage |
Повторный запуск без повторного ввода пароля (PersistTokens = true) |
Токены шифруются протектором: ProtectTokensInMemory = true шифрует их в памяти, а
PersistTokens = true вместе с TokenStoragePath сохраняет зашифрованный файл. Протектор создаётся
фабрикой TokenProtectorFactory
на базе Data Protection.
События состояния авторизации
Подписка на события позволяет приложению реагировать на вход и выход без опроса состояния.
var isAuthenticated = false;
var signedOut = false;
factory.Authorization.AuthenticationStateChanged += (_, value) => isAuthenticated = value;
factory.Authorization.SignedOut += (_, _) => signedOut = true;
await factory.Authorization.SignInAsync(signInRequest);
if (!isAuthenticated)
throw new InvalidOperationException("Не удалось авторизоваться.");
| Событие | Тип | Момент |
|---|---|---|
AuthenticationStateChanged |
EventHandler<bool> |
Изменилось состояние авторизации |
SignedOut |
EventHandler |
Выход выполнен |
Обработка ошибок
Методы API вызывают EnsureSuccessStatusCode(), поэтому при неуспешном ответе выбрасывается
HttpRequestException со статус-кодом в StatusCode. При ошибке входа
AuthorizationService очищает
сохранённые токены.
using System.Net;
using System.Net.Http;
try
{
await factory.Authorization.SignInAsync(signInRequest);
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
// Неверные учётные данные — вызывающий код решает, повторить вход или отказать.
throw new InvalidOperationException("Авторизация отклонена: проверьте логин и пароль.", ex);
}
catch (HttpRequestException ex)
{
// Прочие ошибки транспорта пробрасываются с сохранением статус-кода и текста.
throw new InvalidOperationException($"Ошибка обращения к web-API: {ex.StatusCode}.", ex);
}
Ссылки
- Пример применения в составе SDK:
Samples.WebApi.Authorization(SDK/Web/Samples/Server/Samples.WebApi.Authorization). - Авторизация с помощью общего сервиса авторизации.
- Миграция на клиентскую библиотеку Ascon.Polynom.Web.Api.Client.