83 lines
3.2 KiB
C#
83 lines
3.2 KiB
C#
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using CM.Authentication.Abstractions;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace CM.Authentication;
|
|
|
|
public class JwtTokenManagerService(IOptions<AuthenticationOptions> options, IHttpClientFactory httpClientFactory, IMemoryCache? memoryCache, ILogger<JwtTokenManagerService>? logger)
|
|
: IJwtTokenManagerService
|
|
{
|
|
private readonly AuthenticationOptions _options = options.Value;
|
|
private readonly JwtTokenCacheOptions _cacheOptions = options.Value.CacheOptions;
|
|
private readonly HttpClient _httpClient = httpClientFactory.CreateClient();
|
|
private readonly TimeSpan _cacheExpirationOffset = TimeSpan.FromSeconds(options.Value.CacheOptions.ExpirationOffset);
|
|
|
|
public async Task<JwtTokenResult> GetTokenAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
if (memoryCache != null)
|
|
{
|
|
var memoryGetValueResult = memoryCache.TryGetValue(_cacheOptions.CacheKey, out JwtTokenResult? cachedToken);
|
|
if (memoryGetValueResult && null != cachedToken)
|
|
{
|
|
return cachedToken;
|
|
}
|
|
}
|
|
|
|
var formContentKeyValues = new List<KeyValuePair<string, string>>()
|
|
{
|
|
new ("grant_type", "password"),
|
|
new ("username", _options.Username),
|
|
new ("password", _options.Password)
|
|
};
|
|
|
|
var formContent = new FormUrlEncodedContent(formContentKeyValues);
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, _options.TokenEndpoint)
|
|
{
|
|
Content = formContent
|
|
};
|
|
|
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken);
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
var tokenResponse = await response.Content.ReadFromJsonAsync(JwtTokenManagerJsonContext.Default.JwtTokenResponse, cancellationToken);
|
|
|
|
if (tokenResponse is null)
|
|
throw new InvalidOperationException("Failed to deserialize the token response content.");
|
|
|
|
var parsedResult = Enum.TryParse<TokenType>(tokenResponse.TokenType, true, out var tokenType);
|
|
|
|
if (parsedResult == false)
|
|
throw new InvalidOperationException($"Unsupported token type: {tokenResponse.TokenType}");
|
|
|
|
var now = DateTime.UtcNow;
|
|
var expiration = TimeSpan.FromSeconds(tokenResponse.ExpiresIn);
|
|
var expiresAt = now.Add(expiration);
|
|
var result = new JwtTokenResult
|
|
{
|
|
AccessToken = tokenResponse.AccessToken,
|
|
TokenType = tokenType,
|
|
Accounts = tokenResponse.Accounts,
|
|
RefreshToken = tokenResponse.RefreshToken,
|
|
ExpiresIn = tokenResponse.ExpiresIn,
|
|
|
|
ExpiresAt = expiresAt,
|
|
};
|
|
|
|
if (null != memoryCache && expiration < _cacheExpirationOffset)
|
|
{
|
|
logger?.LogWarning("Caching is enable but the token expiration time [{expiration}] is less than the expiration offset [{cacheExpirationOffset}]. Caching is ignored, please validate your authorization server configuration and the {className} cache expiration offset configuration",
|
|
expiration, _cacheExpirationOffset, nameof(JwtTokenManagerService));
|
|
}
|
|
else
|
|
memoryCache?.Set(_cacheOptions.CacheKey, result, expiration.Subtract(_cacheExpirationOffset));
|
|
|
|
return result;
|
|
}
|
|
} |