118 lines
4.0 KiB
C#
118 lines
4.0 KiB
C#
using IdentityModel.Client;
|
|
using PostFundManagement.Domain.Configuration.Security;
|
|
|
|
namespace PostFundManagement.Application.Handlers;
|
|
|
|
public class ClientCredentialsTokenHandler(ILogger<ClientCredentialsTokenHandler> logger,
|
|
IOptions<MachineIdentitySettings> options) : DelegatingHandler
|
|
{
|
|
private readonly MachineIdentitySettings _settings = options.Value;
|
|
private string? _cachedToken;
|
|
private DateTime _tokenExpiry = DateTime.MinValue;
|
|
|
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
var token = await GetAccessTokenAsync(cancellationToken);
|
|
|
|
if (string.IsNullOrWhiteSpace(token))
|
|
{
|
|
logger.LogError("Failed to acquire access token for Skoruba API");
|
|
|
|
throw new InvalidOperationException("Unable to acquire access token");
|
|
}
|
|
|
|
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
|
|
|
logger.LogDebug("ClientCredentialsTokenHandler: Added bearer token to request");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error acquiring client credentials token");
|
|
|
|
throw;
|
|
}
|
|
|
|
try
|
|
{
|
|
var response = await base.SendAsync(request, cancellationToken);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var content = await response.Content.ReadAsStringAsync(cancellationToken);
|
|
logger.LogWarning("HTTP request failed with status {StatusCode} for {Method} {RequestUri}. Details: {Details}",
|
|
response.StatusCode, request.Method, request.RequestUri, content);
|
|
|
|
}
|
|
|
|
return response;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error sending HTTP request with client credentials token");
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private async Task<string?> GetAccessTokenAsync(CancellationToken cancellationToken)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(_cachedToken) && DateTime.UtcNow < _tokenExpiry)
|
|
{
|
|
logger.LogDebug("Using cached access token");
|
|
|
|
return _cachedToken;
|
|
}
|
|
|
|
logger.LogDebug("Acquiring new access token from {Authority}", _settings.Authority);
|
|
|
|
using var tokenClient = new HttpClient();
|
|
|
|
try
|
|
{
|
|
if (!_settings.Authority!.EndsWith('/')) _settings.Authority += "/";
|
|
|
|
var scope = _settings.Scope;
|
|
|
|
var tokenRequest = new ClientCredentialsTokenRequest
|
|
{
|
|
Address = $"{_settings.Authority}connect/token",
|
|
ClientId = _settings.ClientId!,
|
|
ClientSecret = _settings.ClientSecret,
|
|
GrantType = "client_credentials",
|
|
Scope = scope,
|
|
};
|
|
|
|
logger.LogDebug("Sending token request to {TokenEndpoint} for client {ClientId} with scope {Scope}",
|
|
tokenRequest.Address, tokenRequest.ClientId, tokenRequest.Scope);
|
|
|
|
var response = await tokenClient.RequestClientCredentialsTokenAsync(tokenRequest, cancellationToken);
|
|
|
|
if (response.IsError)
|
|
{
|
|
logger.LogError("Token request error: {Error} - {ErrorDescription}. Full response: {Raw}",
|
|
response.Error, response.ErrorDescription, response.Raw);
|
|
|
|
return null;
|
|
}
|
|
|
|
_cachedToken = response.AccessToken;
|
|
|
|
var expiresIn = response.ExpiresIn > 0 ? response.ExpiresIn : 3600;
|
|
|
|
_tokenExpiry = DateTime.UtcNow.AddSeconds(expiresIn * 0.9);
|
|
|
|
logger.LogDebug("Successfully acquired access token, expires at {Expiry}", _tokenExpiry);
|
|
|
|
return _cachedToken;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Exception occurred while acquiring client credentials token");
|
|
|
|
throw;
|
|
}
|
|
}
|
|
}
|