/
rezvich
/
Prik
Обзор
Документация
Войти
/
rezvich
/
Prik
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Infrastructure/Auth/RemoteAccessTokenProvider.cs
103 строки
3 KB
waweda299
Прик
08 июн 2026, 15:52
08 июн 2026, 15:52
9ddc25e
Код
Авторство
О чём код?
using Microsoft.Extensions.Logging; using System.Net.Http.Json; using Microsoft.Extensions.Http.Resilience; namespace prik.Infrastructure.Auth { public sealed class RemoteAccessTokenProvider : IAccessTokenProvider { private readonly HttpClient _http; private readonly string? _apiKey; private readonly ILogger<RemoteAccessTokenProvider> _log; private readonly SemaphoreSlim _lock = new(1, 1); private string? _token; private DateTimeOffset _expiresAtUtc = DateTimeOffset.MinValue; private sealed class TokenDto { public string? access_token { get; set; } public int? expires_in { get; set; } } public RemoteAccessTokenProvider(HttpClient http, string? apiKey, ILogger<RemoteAccessTokenProvider> log) { _http = http; _apiKey = apiKey; _log = log; } public void Invalidate() { _lock.Wait(); try { _token = null; _expiresAtUtc = DateTimeOffset.MinValue; } finally { _lock.Release(); } } public async Task<string> GetAccessTokenAsync(CancellationToken ct = default) { var now = DateTimeOffset.UtcNow; if (!string.IsNullOrWhiteSpace(_token) && now < _expiresAtUtc) { return _token!; } await _lock.WaitAsync(ct); try { now = DateTimeOffset.UtcNow; if (!string.IsNullOrWhiteSpace(_token) && now < _expiresAtUtc) { return _token!; } using var req = new HttpRequestMessage(HttpMethod.Get, "/iam/access-token"); if (!string.IsNullOrWhiteSpace(_apiKey)) { req.Headers.TryAddWithoutValidation("X-Api-Key", _apiKey); } HttpResponseMessage resp; try { resp = await _http.SendAsync(req, ct); } catch (TaskCanceledException ex) when (!ct.IsCancellationRequested) { throw new TimeoutException( $"TokenService timeout (HttpClient.Timeout={_http.Timeout.TotalSeconds:0}s).", ex); } using (resp) { resp.EnsureSuccessStatusCode(); var dto = await resp.Content.ReadFromJsonAsync<TokenDto>(cancellationToken: ct); if (dto == null || string.IsNullOrWhiteSpace(dto.access_token)) { throw new InvalidOperationException("TokenService вернул пустой access_token"); } _token = dto.access_token; var ttl = dto.expires_in.GetValueOrDefault(300); _expiresAtUtc = now.AddSeconds(Math.Max(10, ttl - 5)); return _token!; } } finally { _lock.Release(); } } } }