diff --git a/Auth/SystemAuthenticator.cs b/Auth/SystemAuthenticator.cs index d47122f..8026dee 100644 --- a/Auth/SystemAuthenticator.cs +++ b/Auth/SystemAuthenticator.cs @@ -13,12 +13,10 @@ public class SystemAuthenticator : IDisposable private readonly HttpClient _httpClient; private readonly ConfigurationManager _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( $"{_clientData.Authority}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever() @@ -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, }; } diff --git a/Auth/UserAuthenticator.cs b/Auth/UserAuthenticator.cs index 1ac0c3d..a3bd867 100644 --- a/Auth/UserAuthenticator.cs +++ b/Auth/UserAuthenticator.cs @@ -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 LoginAndGetTokens(string[]? resources = null) { var resourceTokens = new List(); - var latestRefreshToken = string.Empty; ValidateResources(resources); @@ -34,10 +31,8 @@ public async Task 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 @@ -56,12 +51,12 @@ public async Task 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. @@ -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 { diff --git a/Auth/Utils/AuthHttpClient.cs b/Auth/Utils/AuthHttpClient.cs index 0f35468..016c85e 100644 --- a/Auth/Utils/AuthHttpClient.cs +++ b/Auth/Utils/AuthHttpClient.cs @@ -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; @@ -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 Get(string uri, string? accessToken = null, IDictionary? headers = null) + public async Task Get(string uri, string? accessToken = null, IDictionary? headers = null) { - string json = await Get(uri, accessToken, headers); - - return Deserialize(json); - } - - public async Task Post(string uri, TRequestType body, string? accessToken = null, IDictionary? headers = null) - { - string jsonBody = body is string str ? str : JsonSerializer.Serialize(body, JsonSerializerOptions); - string jsonResponse = await Post(uri, jsonBody, accessToken, headers); - - return Deserialize(jsonResponse); - } - - public async Task Put(string uri, TRequestType body, string? accessToken = null, IDictionary? headers = null) - { - string jsonBody = JsonSerializer.Serialize(body, JsonSerializerOptions); - string jsonResponse = await Put(uri, jsonBody, accessToken, headers); - - return Deserialize(jsonResponse); - } + var requestMessage = CreateRequestMessage(uri, accessToken, headers, HttpMethod.Get); + var response = await _httpClient.SendAsync(requestMessage); - public async Task Put(string uri, TRequestType body, string? accessToken = null, IDictionary? headers = null) - { - string jsonBody = JsonSerializer.Serialize(body, JsonSerializerOptions); - var _ = await Put(uri, jsonBody, accessToken, headers); + return await GetResponseMessage(response); } - public async Task Get(string uri, string? accessToken = null, IDictionary? headers = null) + public async Task Post(string uri, TRequest body, string? accessToken = null, IDictionary? 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(response); } - public async Task Post(string uri, string body, string? accessToken = null, IDictionary? headers = null) + public async Task Put(string uri, TRequest body, string? accessToken = null, IDictionary? headers = null) { - return await SendWithContent(uri, body, accessToken, headers, HttpMethod.Post); - } - - public async Task Put(string uri, string body, string? accessToken = null, IDictionary? headers = null) - { - return await SendWithContent(uri, body, accessToken, headers, HttpMethod.Put); - } - - private async Task SendWithContent(string uri, string body, string? accessToken, IDictionary? 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? headers, HttpMethod method) @@ -106,28 +71,31 @@ private HttpRequestMessage CreateRequestMessage(string uri, string? accessToken, return requestMessage; } - private static async Task GetResponseMessage(HttpResponseMessage response) + private void SetJsonContent(HttpRequestMessage requestMessage, TRequest body) { - string responseMessage = await response.Content.ReadAsStringAsync(); + requestMessage.Content = JsonContent.Create(body, options: JsonSerializerOptions); + } - if (!response.IsSuccessStatusCode) + private static async Task GetResponseMessage(HttpResponseMessage response) + { + await EnsureSuccess(response); + + var responseBody = await response.Content.ReadFromJsonAsync(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(string json) + private static async Task EnsureSuccess(HttpResponseMessage response) { - var deserialized = JsonSerializer.Deserialize(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()