Skip to content

Commit

Permalink
Simplify and clean up some things
Browse files Browse the repository at this point in the history
  • Loading branch information
Gachapen committed Jan 21, 2025
1 parent 5f1e845 commit 423a03c
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 93 deletions.
23 changes: 4 additions & 19 deletions Auth/SystemAuthenticator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ public class SystemAuthenticator : IDisposable
private readonly HttpClient _httpClient;
private readonly ConfigurationManager<OpenIdConnectConfiguration> _oidcConfigManager;

public SystemAuthenticator(SystemClientData clientData, bool logHttp = false)
public SystemAuthenticator(SystemClientData clientData)
{
_clientData = clientData;
_httpClient = logHttp
? new HttpClient(new RawHttpLoggingHandler(new HttpClientHandler()))
: new HttpClient();
_httpClient = new HttpClient();
_oidcConfigManager = new ConfigurationManager<OpenIdConnectConfiguration>(
$"{_clientData.Authority}/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever()
Expand Down Expand Up @@ -59,28 +57,15 @@ private bool RequiresDPoPNonce(TokenResponse tokenResponse)

private ClientCredentialsTokenRequest CreateClientCredentialsTokenRequest(string tokenEndpoint, string? dPoPNonce = null)
{
var request = CreateTokenRequestBase(tokenEndpoint, dPoPNonce);
return new ClientCredentialsTokenRequest
{
Address = request.Address,
ClientAssertion = request.ClientAssertion,
ClientId = request.ClientId,
ClientCredentialStyle = request.ClientCredentialStyle,
DPoPProofToken = request.DPoPProofToken,
Scope = string.Join(" ", _clientData.Scopes),
GrantType = OidcConstants.GrantTypes.ClientCredentials,
};
}

private ProtocolRequest CreateTokenRequestBase(string tokenEndpoint, string? dPoPNonce = null)
{
return new ProtocolRequest
{
Address = tokenEndpoint,
ClientAssertion = ClientAssertionBuilder.Build(_clientData.ClientId, _clientData.Jwk.PublicAndPrivateValue, _clientData.Authority),
ClientId = _clientData.ClientId,
ClientCredentialStyle = ClientCredentialStyle.PostBody,
DPoPProofToken = _clientData.UseDPoP ? DPoPProofBuilder.CreateDPoPProof(tokenEndpoint, "POST", _clientData.Jwk, dPoPNonce: dPoPNonce) : null,
Scope = string.Join(" ", _clientData.Scopes),
GrantType = OidcConstants.GrantTypes.ClientCredentials,
};
}

Expand Down
19 changes: 7 additions & 12 deletions Auth/UserAuthenticator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,16 @@ public sealed class UserAuthenticator : IDisposable
private readonly HttpClient _httpClient;
private readonly OidcClient _oidcClient;

public UserAuthenticator(UserClientData clientData, string htmlTitle, string htmlBody, bool logHttp = false)
public UserAuthenticator(UserClientData clientData, string htmlTitle, string htmlBody)
{
_clientData = clientData;
_httpClient = logHttp
? new HttpClient(new RawHttpLoggingHandler(new HttpClientHandler()))
: new HttpClient();
_httpClient = new HttpClient();
_oidcClient = CreateOidcClient(htmlTitle, htmlBody);
}

public async Task<ResourceTokens> LoginAndGetTokens(string[]? resources = null)
{
var resourceTokens = new List<ResourceToken>();
var latestRefreshToken = string.Empty;

ValidateResources(resources);

Expand All @@ -34,10 +31,8 @@ public async Task<ResourceTokens> LoginAndGetTokens(string[]? resources = null)
// Uses OidcClient to login the user and retrieve tokens.
// Pushed Authorization Request (PAR) is automatically used by OidcClient.
// Use the Resource-parameter to indicate which APIs you want tokens for
// Use the Scope-parameter to indicate which scopes you want for these APIs

var resourcesToGetTokensFor = resources?.Length > 0 ? resources : _clientData.Resources.Select(r => r.Name).ToArray();

var firstResource = resourcesToGetTokensFor.First();

var loginRequest = new LoginRequest
Expand All @@ -56,12 +51,12 @@ public async Task<ResourceTokens> LoginAndGetTokens(string[]? resources = null)

resourceTokens.Add(new ResourceToken(firstResource, loginResult.AccessToken));

latestRefreshToken = loginResult.RefreshToken;
var latestRefreshToken = loginResult.RefreshToken;

// 2. Using the refresh token to get an access token for API N
//////////////////////////////////////////////////////////////
// Now we want a second access token to be used for API N
// Again we use the /token-endpoint, but now we use the refresh token
// Now we want a second access token to be used for API N.
// Again we use the /token-endpoint, but now we use the refresh token.
// The Resource parameter indicates that we want a token for API N.
// We won't use a refresh token to get an access token for the first
// resource – we received that token logging in.
Expand Down Expand Up @@ -97,10 +92,10 @@ [new ResourceToken(resource, refreshResult.AccessToken)],

private OidcClient CreateOidcClient(string htmlTitle, string htmlBody)
{
var resourceScope = string.Join(" ", _clientData.Resources.Select(r => string.Join(" ", r.Scopes)));
var apiScopes = string.Join(" ", _clientData.Resources.Select(r => string.Join(" ", r.Scopes)));

var redirectUri = $"{_clientData.RedirectHost}{_clientData.RedirectPath}";
var scope = $"openid offline_access {resourceScope}";
var scope = $"openid offline_access {apiScopes}";

var options = new OidcClientOptions
{
Expand Down
92 changes: 30 additions & 62 deletions Auth/Utils/AuthHttpClient.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Common.Models;
using IdentityModel.Client;
using System.Net.Http.Headers;
using System.Text;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;

Expand All @@ -12,71 +11,37 @@ public class AuthHttpClient : IDisposable
private readonly HttpClient _httpClient;
private readonly JwkWithMetadata? _dPoPKey;

public AuthHttpClient(JwkWithMetadata? dPoPKey = null, bool logHttp = false)
public AuthHttpClient(JwkWithMetadata? dPoPKey = null)
{
_httpClient = logHttp
? new HttpClient(new RawHttpLoggingHandler(new HttpClientHandler()))
: new HttpClient();
_httpClient = new HttpClient();
_dPoPKey = dPoPKey;
}

public async Task<T> Get<T>(string uri, string? accessToken = null, IDictionary<string, string>? headers = null)
public async Task<TResponse> Get<TResponse>(string uri, string? accessToken = null, IDictionary<string, string>? headers = null)
{
string json = await Get(uri, accessToken, headers);

return Deserialize<T>(json);
}

public async Task<TResponseType> Post<TRequestType, TResponseType>(string uri, TRequestType body, string? accessToken = null, IDictionary<string, string>? headers = null)
{
string jsonBody = body is string str ? str : JsonSerializer.Serialize(body, JsonSerializerOptions);
string jsonResponse = await Post(uri, jsonBody, accessToken, headers);

return Deserialize<TResponseType>(jsonResponse);
}

public async Task<TResponseType> Put<TRequestType, TResponseType>(string uri, TRequestType body, string? accessToken = null, IDictionary<string, string>? headers = null)
{
string jsonBody = JsonSerializer.Serialize(body, JsonSerializerOptions);
string jsonResponse = await Put(uri, jsonBody, accessToken, headers);

return Deserialize<TResponseType>(jsonResponse);
}
var requestMessage = CreateRequestMessage(uri, accessToken, headers, HttpMethod.Get);
var response = await _httpClient.SendAsync(requestMessage);

public async Task Put<TRequestType>(string uri, TRequestType body, string? accessToken = null, IDictionary<string, string>? headers = null)
{
string jsonBody = JsonSerializer.Serialize(body, JsonSerializerOptions);
var _ = await Put(uri, jsonBody, accessToken, headers);
return await GetResponseMessage<TResponse>(response);
}

public async Task<string> Get(string uri, string? accessToken = null, IDictionary<string, string>? headers = null)
public async Task<TResponse> Post<TRequest, TResponse>(string uri, TRequest body, string? accessToken = null, IDictionary<string, string>? headers = null)
{
using var requestMessage = CreateRequestMessage(uri, accessToken, headers, HttpMethod.Get);
var requestMessage = CreateRequestMessage(uri, accessToken, headers, HttpMethod.Post);
SetJsonContent(requestMessage, body);

var response = await _httpClient.SendAsync(requestMessage);

return await GetResponseMessage(response);
return await GetResponseMessage<TResponse>(response);
}

public async Task<string> Post(string uri, string body, string? accessToken = null, IDictionary<string, string>? headers = null)
public async Task Put<TRequest>(string uri, TRequest body, string? accessToken = null, IDictionary<string, string>? headers = null)
{
return await SendWithContent(uri, body, accessToken, headers, HttpMethod.Post);
}

public async Task<string> Put(string uri, string body, string? accessToken = null, IDictionary<string, string>? headers = null)
{
return await SendWithContent(uri, body, accessToken, headers, HttpMethod.Put);
}

private async Task<string> SendWithContent(string uri, string body, string? accessToken, IDictionary<string, string>? headers, HttpMethod method)
{
var requestMessage = CreateRequestMessage(uri, accessToken, headers, method);

requestMessage.Content = new StringContent(body, Encoding.UTF8, MediaTypeHeaderValue.Parse("application/json"));
var requestMessage = CreateRequestMessage(uri, accessToken, headers, HttpMethod.Put);
SetJsonContent(requestMessage, body);

var response = await _httpClient.SendAsync(requestMessage);

return await GetResponseMessage(response);
await EnsureSuccess(response);
}

private HttpRequestMessage CreateRequestMessage(string uri, string? accessToken, IDictionary<string, string>? headers, HttpMethod method)
Expand Down Expand Up @@ -106,28 +71,31 @@ private HttpRequestMessage CreateRequestMessage(string uri, string? accessToken,
return requestMessage;
}

private static async Task<string> GetResponseMessage(HttpResponseMessage response)
private void SetJsonContent<TRequest>(HttpRequestMessage requestMessage, TRequest body)
{
string responseMessage = await response.Content.ReadAsStringAsync();
requestMessage.Content = JsonContent.Create(body, options: JsonSerializerOptions);
}

if (!response.IsSuccessStatusCode)
private static async Task<TResponse> GetResponseMessage<TResponse>(HttpResponseMessage response)
{
await EnsureSuccess(response);

var responseBody = await response.Content.ReadFromJsonAsync<TResponse>(options: JsonSerializerOptions);
if (responseBody == null)
{
throw new Exception($"Http request failed. Code: {response.StatusCode} Content: {responseMessage}");
throw new Exception("Response contained null value");
}

return responseMessage;
return responseBody;
}

private static T Deserialize<T>(string json)
private static async Task EnsureSuccess(HttpResponseMessage response)
{
var deserialized = JsonSerializer.Deserialize<T>(json, options: JsonSerializerOptions);

if (deserialized == null)
if (!response.IsSuccessStatusCode)
{
throw new Exception($"{json} was deserialized as null.");
var responseMessage = await response.Content.ReadAsStringAsync();
throw new Exception($"Http request failed. Code: {response.StatusCode} Content: {responseMessage}");
}

return deserialized;
}

public void Dispose()
Expand Down

0 comments on commit 423a03c

Please sign in to comment.