dotnet-jwt-token-manager/OpenHarbor.JwtTokenManager/JwtTokenManagerService.cs

81 lines
3.2 KiB
C#

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using OpenHarbor.JwtTokenManager.Abstractions;
namespace OpenHarbor.JwtTokenManager;
public class JwtTokenManagerService(JwtTokenManagerOptions options, IHttpClientFactory httpClientFactory, ILogger<JwtTokenManagerService>? logger, IMemoryCache? memoryCache, JwtTokenManagerCacheOptions cacheOptions)
: IJwtTokenManagerService
{
private readonly HttpClient _httpClient = httpClientFactory.CreateClient();
private readonly TimeSpan _cacheExpirationOffset = TimeSpan.FromSeconds(cacheOptions.ExpirationOffset);
private readonly string _scopes = string.Join(" ", options.Scopes);
public async Task<JwtTokenManagerResult> GetTokenAsync(CancellationToken cancellationToken = default)
{
if (memoryCache != null)
{
var memoryGetValueResult = memoryCache.TryGetValue(cacheOptions.CacheKey, out JwtTokenManagerResult? cachedToken);
if (memoryGetValueResult && null != cachedToken)
{
return cachedToken;
}
}
var formContent = new FormUrlEncodedContent([
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", options.ClientId),
new KeyValuePair<string, string>("client_secret", options.ClientSecret),
new KeyValuePair<string, string>("scopes", string.Join(" ", _scopes))
]);
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 == null)
{
throw new InvalidOperationException("Failed to deserialize the token response content.");
}
var parsedResult = Enum.TryParse<TokenType>(tokenResponse.TokenType, 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 JwtTokenManagerResult
{
AccessToken = tokenResponse.AccessToken,
TokenType = tokenType,
ExpiresAt = expiresAt,
ExpiresIn = tokenResponse.ExpiresIn,
};
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;
}
}