Skip to content

Commit

Permalink
Merge pull request #25 from DuendeSoftware/brock/tests
Browse files Browse the repository at this point in the history
add simple integration test to exercise some of the dpop client code
  • Loading branch information
leastprivilege authored Mar 31, 2023
2 parents c42f73d + 185f407 commit 811ea10
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -22,13 +23,15 @@ public class OpenIdConnectClientAccessTokenHandler : AccessTokenHandler
/// <param name="dPoPProofService"></param>
/// <param name="dPoPNonceStore"></param>
/// <param name="httpContextAccessor"></param>
/// <param name="logger"></param>
/// <param name="parameters"></param>
public OpenIdConnectClientAccessTokenHandler(
IDPoPProofService dPoPProofService,
IDPoPNonceStore dPoPNonceStore,
IHttpContextAccessor httpContextAccessor,
ILogger<OpenIdConnectClientAccessTokenHandler> logger,
UserTokenRequestParameters? parameters = null)
: base(dPoPProofService, dPoPNonceStore)
: base(dPoPProofService, dPoPNonceStore, logger)
{
_httpContextAccessor = httpContextAccessor;
_parameters = parameters ?? new UserTokenRequestParameters();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -143,8 +144,9 @@ public static IHttpClientBuilder AddUserAccessTokenHandler(
var dpopService = provider.GetRequiredService<IDPoPProofService>();
var dpopNonceStore = provider.GetRequiredService<IDPoPNonceStore>();
var contextAccessor = provider.GetRequiredService<IHttpContextAccessor>();
return new OpenIdConnectUserAccessTokenHandler(dpopService, dpopNonceStore, contextAccessor, parameters);
var logger = provider.GetRequiredService<ILogger<OpenIdConnectClientAccessTokenHandler>>();
return new OpenIdConnectUserAccessTokenHandler(dpopService, dpopNonceStore, contextAccessor, logger, parameters);
});
}

Expand All @@ -163,8 +165,9 @@ public static IHttpClientBuilder AddClientAccessTokenHandler(
var dpopService = provider.GetRequiredService<IDPoPProofService>();
var dpopNonceStore = provider.GetRequiredService<IDPoPNonceStore>();
var contextAccessor = provider.GetRequiredService<IHttpContextAccessor>();
var logger = provider.GetRequiredService<ILogger<OpenIdConnectClientAccessTokenHandler>>();
return new OpenIdConnectClientAccessTokenHandler(dpopService, dpopNonceStore, contextAccessor, parameters);
return new OpenIdConnectClientAccessTokenHandler(dpopService, dpopNonceStore, contextAccessor, logger, parameters);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -22,13 +23,15 @@ public class OpenIdConnectUserAccessTokenHandler : AccessTokenHandler
/// <param name="dPoPProofService"></param>
/// <param name="dPoPNonceStore"></param>
/// <param name="httpContextAccessor"></param>
/// <param name="logger"></param>
/// <param name="parameters"></param>
public OpenIdConnectUserAccessTokenHandler(
IDPoPProofService dPoPProofService,
IDPoPNonceStore dPoPNonceStore,
IHttpContextAccessor httpContextAccessor,
ILogger<OpenIdConnectClientAccessTokenHandler> logger,
UserTokenRequestParameters? parameters = null)
: base(dPoPProofService, dPoPNonceStore)
: base(dPoPProofService, dPoPNonceStore, logger)
{
_httpContextAccessor = httpContextAccessor;
_parameters = parameters ?? new UserTokenRequestParameters();
Expand Down
21 changes: 19 additions & 2 deletions src/Duende.AccessTokenManagement/AccessTokenHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using IdentityModel.Client;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -16,18 +17,22 @@ public abstract class AccessTokenHandler : DelegatingHandler
{
private readonly IDPoPProofService _dPoPProofService;
private readonly IDPoPNonceStore _dPoPNonceStore;
private readonly ILogger _logger;

/// <summary>
/// ctor
/// </summary>
/// <param name="dPoPProofService"></param>
/// <param name="dPoPNonceStore"></param>
/// <param name="logger"></param>
public AccessTokenHandler(
IDPoPProofService dPoPProofService,
IDPoPNonceStore dPoPNonceStore)
IDPoPNonceStore dPoPNonceStore,
ILogger logger)
{
_dPoPProofService = dPoPProofService;
_dPoPNonceStore = dPoPNonceStore;
_logger = logger;
}

/// <summary>
Expand All @@ -52,7 +57,11 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
response.Dispose();

// if it's a DPoP nonce error, we don't need to obtain a new access token
var force = response.IsDPoPNonceError();
var force = !response.IsDPoPNonceError();
if (!force && !string.IsNullOrEmpty(dPoPNonce))
{
_logger.LogDebug("DPoP nonce error invoking endpoint: {url}, retrying using new nonce", request.RequestUri?.AbsoluteUri.ToString());
}

await SetTokenAsync(request, forceRenewal: force, cancellationToken, dPoPNonce).ConfigureAwait(false);
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
Expand All @@ -79,6 +88,8 @@ protected virtual async Task SetTokenAsync(HttpRequestMessage request, bool forc

if (!string.IsNullOrWhiteSpace(token?.AccessToken))
{
_logger.LogDebug("Sending access token in request to endpoint: {url}", request.RequestUri?.AbsoluteUri.ToString());

var scheme = token.AccessTokenType ?? AuthenticationSchemes.AuthorizationHeaderBearer;

if (!string.IsNullOrWhiteSpace(token.DPoPJsonWebKey))
Expand Down Expand Up @@ -119,9 +130,15 @@ protected virtual async Task<bool> SetDPoPProofTokenAsync(HttpRequestMessage req

if (proofToken != null)
{
_logger.LogDebug("Sending DPoP proof token in request to endpoint: {url}", request.RequestUri?.AbsoluteUri.ToString());

request.SetDPoPProofToken(proofToken.ProofToken);
return true;
}
else
{
_logger.LogDebug("No DPoP proof token in request to endpoint: {url}", request.RequestUri?.AbsoluteUri.ToString());
}
}

return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ public virtual async Task<ClientCredentialsToken> RequestToken(
var key = await _dPoPKeyMaterialService.GetKeyAsync(clientName);
if (key != null)
{
_logger.LogDebug("Creating DPoP proof token for token request.");

var proof = await _dPoPProofService.CreateProofTokenAsync(new DPoPProofRequest
{
Url = request.Address!,
Expand Down Expand Up @@ -145,6 +147,8 @@ public virtual async Task<ClientCredentialsToken> RequestToken(

if (response.IsError && response.Error == OidcConstants.TokenErrors.UseDPoPNonce && key != null && response.DPoPNonce != null)
{
_logger.LogDebug("Token request failed with DPoP nonce error. Retrying with new nonce.");

var proof = await _dPoPProofService.CreateProofTokenAsync(new DPoPProofRequest
{
Url = request.Address!,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -20,13 +21,15 @@ public class ClientCredentialsTokenHandler : AccessTokenHandler
/// <param name="dPoPProofService"></param>
/// <param name="dPoPNonceStore"></param>
/// <param name="accessTokenManagementService">The Access Token Management Service</param>
/// <param name="logger"></param>
/// <param name="tokenClientName">The name of the token client configuration</param>
public ClientCredentialsTokenHandler(
IDPoPProofService dPoPProofService,
IDPoPNonceStore dPoPNonceStore,
IClientCredentialsTokenManagementService accessTokenManagementService,
ILogger<ClientCredentialsTokenHandler> logger,
string tokenClientName)
: base(dPoPProofService, dPoPNonceStore)
: base(dPoPProofService, dPoPNonceStore, logger)
{
_accessTokenManagementService = accessTokenManagementService;
_tokenClientName = tokenClientName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Net.Http;
using Duende.AccessTokenManagement;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;

namespace Microsoft.Extensions.DependencyInjection;

Expand Down Expand Up @@ -138,8 +139,9 @@ public static IHttpClientBuilder AddClientCredentialsTokenHandler(
var dpopService = provider.GetRequiredService<IDPoPProofService>();
var dpopNonceStore = provider.GetRequiredService<IDPoPNonceStore>();
var accessTokenManagementService = provider.GetRequiredService<IClientCredentialsTokenManagementService>();
var logger = provider.GetRequiredService<ILogger<ClientCredentialsTokenHandler>>();
return new ClientCredentialsTokenHandler(dpopService, dpopNonceStore, accessTokenManagementService, tokenClientName);
return new ClientCredentialsTokenHandler(dpopService, dpopNonceStore, accessTokenManagementService, logger, tokenClientName);
});
}
}
2 changes: 1 addition & 1 deletion src/Duende.AccessTokenManagement/DPoPExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public static bool IsDPoPNonceError(this HttpResponseMessage response)
var parts = x.Split('=', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 2 && parts[0] == OidcConstants.TokenResponse.Error)
{
return parts[1];
return parts[1].Trim('"');
}
return null;
}).Where(x => x != null).FirstOrDefault();
Expand Down
Loading

0 comments on commit 811ea10

Please sign in to comment.