Skip to content

Commit 3cebfeb

Browse files
authored
Merge pull request #300 from DuendeSoftware/pg/http-request-context
Allow a default ITokenRequestCustomizer to be registered
2 parents e2ed624 + a48ed81 commit 3cebfeb

27 files changed

+1276
-448
lines changed

access-token-management/src/AccessTokenManagement.OpenIdConnect/HttpContextExtensions.cs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ public static async Task<TokenResult<UserToken>> GetUserAccessTokenAsync(
2828
CT ct = default)
2929
{
3030
var service = httpContext.RequestServices.GetRequiredService<IUserTokenManager>();
31+
var requestParameters = await ApplyTokenRequestCustomizationAsync(httpContext, parameters, ct);
3132

32-
return await service.GetAccessTokenAsync(httpContext.User, parameters, ct).ConfigureAwait(false);
33+
return await service.GetAccessTokenAsync(httpContext.User, requestParameters, ct).ConfigureAwait(false);
3334
}
3435

3536
/// <summary>
@@ -45,8 +46,9 @@ public static async Task RevokeRefreshTokenAsync(
4546
CT ct = default)
4647
{
4748
var service = httpContext.RequestServices.GetRequiredService<IUserTokenManager>();
49+
var requestParameters = await ApplyTokenRequestCustomizationAsync(httpContext, parameters, ct);
4850

49-
await service.RevokeRefreshTokenAsync(httpContext.User, parameters, ct).ConfigureAwait(false);
51+
await service.RevokeRefreshTokenAsync(httpContext.User, requestParameters, ct).ConfigureAwait(false);
5052
}
5153

5254
/// <summary>
@@ -72,20 +74,49 @@ public static async Task<TokenResult<ClientCredentialsToken>> GetClientAccessTok
7274
var defaultScheme = await schemes.GetDefaultChallengeSchemeAsync().ConfigureAwait(false);
7375
if (defaultScheme == null)
7476
{
75-
throw new InvalidOperationException("Cannot retrieve client access token. No scheme was provided and default challenge scheme was not set.");
77+
throw new InvalidOperationException(
78+
"Cannot retrieve client access token. No scheme was provided and default challenge scheme was not set.");
7679
}
7780

7881
schemeName = Scheme.Parse(defaultScheme.Name);
7982
}
8083

84+
var requestParameters = await ApplyTokenRequestCustomizationAsync(httpContext, parameters, ct);
85+
8186
return await service.GetAccessTokenAsync(
8287
schemeName.Value.ToClientName(),
83-
parameters,
88+
requestParameters,
8489
ct).ConfigureAwait(false);
8590
}
8691

92+
private static async Task<UserTokenRequestParameters> ApplyTokenRequestCustomizationAsync(
93+
HttpContext httpContext,
94+
UserTokenRequestParameters? parameters,
95+
CT ct)
96+
{
97+
var baseParameters = parameters ?? new UserTokenRequestParameters();
98+
var tokenRequestCustomizer = httpContext.RequestServices.GetService<ITokenRequestCustomizer>();
99+
100+
var customizedParameters = tokenRequestCustomizer != null
101+
? await tokenRequestCustomizer.Customize(httpContext.Request.ToHttpRequestContext(), baseParameters, ct)
102+
: baseParameters;
103+
104+
return baseParameters with
105+
{
106+
Scope = customizedParameters.Scope,
107+
Resource = customizedParameters.Resource,
108+
Parameters = customizedParameters.Parameters,
109+
Assertion = customizedParameters.Assertion,
110+
Context = customizedParameters.Context,
111+
ForceTokenRenewal = customizedParameters.ForceTokenRenewal
112+
};
113+
}
114+
87115
const string AuthenticationPropertiesDPoPKey = ".Token.dpop_proof_key";
88-
internal static void SetProofKey(this AuthenticationProperties properties, DPoPProofKey dpopProofKey) => properties.Items[AuthenticationPropertiesDPoPKey] = dpopProofKey.ToString();
116+
117+
internal static void SetProofKey(this AuthenticationProperties properties, DPoPProofKey dpopProofKey) =>
118+
properties.Items[AuthenticationPropertiesDPoPKey] = dpopProofKey.ToString();
119+
89120
internal static DPoPProofKey? GetProofKey(this AuthenticationProperties properties)
90121
{
91122
if (properties.Items.TryGetValue(AuthenticationPropertiesDPoPKey, out var key))
@@ -97,17 +128,22 @@ public static async Task<TokenResult<ClientCredentialsToken>> GetClientAccessTok
97128

98129
return DPoPProofKey.Parse(key);
99130
}
131+
100132
return null;
101133
}
102134

103135
const string HttpContextDPoPKey = "dpop_proof_key";
104-
internal static void SetCodeExchangeDPoPKey(this HttpContext context, DPoPProofKey dpopProofKey) => context.Items[HttpContextDPoPKey] = dpopProofKey;
136+
137+
internal static void SetCodeExchangeDPoPKey(this HttpContext context, DPoPProofKey dpopProofKey) =>
138+
context.Items[HttpContextDPoPKey] = dpopProofKey;
139+
105140
internal static DPoPProofKey? GetCodeExchangeDPoPKey(this HttpContext context)
106141
{
107142
if (context.Items.TryGetValue(HttpContextDPoPKey, out var item))
108143
{
109144
return item as DPoPProofKey?;
110145
}
146+
111147
return null;
112148
}
113149
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) Duende Software. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
using Microsoft.AspNetCore.Http;
5+
using Microsoft.AspNetCore.Http.Extensions;
6+
7+
namespace Duende.AccessTokenManagement.OpenIdConnect;
8+
9+
internal static class HttpRequestContextExtensions
10+
{
11+
public static HttpRequestContext ToHttpRequestContext(this HttpRequest request) =>
12+
new()
13+
{
14+
Method = request.Method,
15+
RequestUri = new Uri(request.GetEncodedUrl()),
16+
Headers = request.Headers.Select(h => new KeyValuePair<string, IEnumerable<string>>(h.Key, h.Value))
17+
};
18+
}

access-token-management/src/AccessTokenManagement.OpenIdConnect/Internal/OpenIdConnectClientAccessTokenRetriever.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ internal class OpenIdConnectClientAccessTokenRetriever(
3434
};
3535

3636
var customizedParameters = customizer != null
37-
? await customizer.Customize(request, baseParameters, ct)
37+
? await customizer.Customize(request.ToHttpRequestContext(), baseParameters, ct)
3838
: baseParameters;
3939

4040
var userTokenRequestParameters = baseParameters with
4141
{
42-
Scope = customizedParameters.Scope ?? _parameters.Scope,
43-
Resource = customizedParameters.Resource ?? _parameters.Resource,
42+
Scope = customizedParameters.Scope,
43+
Resource = customizedParameters.Resource,
4444
Parameters = customizedParameters.Parameters,
4545
Assertion = customizedParameters.Assertion,
4646
Context = customizedParameters.Context,

access-token-management/src/AccessTokenManagement.OpenIdConnect/Internal/OpenIdConnectUserAccessTokenRetriever.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ internal class OpenIdConnectUserAccessTokenRetriever(
2929
};
3030

3131
var customizedParameters = customizer != null
32-
? await customizer.Customize(request, baseParameters, ct)
32+
? await customizer.Customize(request.ToHttpRequestContext(), baseParameters, ct)
3333
: baseParameters;
3434

3535
var parameters = baseParameters with

access-token-management/src/AccessTokenManagement.OpenIdConnect/ServiceCollectionExtensions.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ private static IHttpClientBuilder AddUserAccessTokenHandlerInternal(
258258
{
259259
var httpContextAccessor = provider.GetRequiredService<IUserAccessor>();
260260
var userTokenManagementService = provider.GetRequiredService<IUserTokenManager>();
261-
var tokenRequestCustomizer = tokenRequestCustomizerFactory?.Invoke(provider);
261+
var tokenRequestCustomizer = tokenRequestCustomizerFactory?.Invoke(provider) ??
262+
provider.GetService<ITokenRequestCustomizer>();
262263

263264
var tokenRetriever = new OpenIdConnectUserAccessTokenRetriever(
264265
httpContextAccessor,
@@ -321,7 +322,8 @@ private static IHttpClientBuilder AddClientAccessTokenHandlerInternal(
321322
var tokenManager = provider.GetRequiredService<IClientCredentialsTokenManager>();
322323
var schemeProvider = provider.GetRequiredService<IAuthenticationSchemeProvider>();
323324
var options = provider.GetRequiredService<IOptions<UserTokenManagementOptions>>();
324-
var tokenRequestCustomizer = tokenRequestCustomizerFactory?.Invoke(provider);
325+
var tokenRequestCustomizer = tokenRequestCustomizerFactory?.Invoke(provider) ??
326+
provider.GetService<ITokenRequestCustomizer>();
325327

326328
var tokenRetriever = new OpenIdConnectClientAccessTokenRetriever(tokenManager,
327329
options,
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) Duende Software. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace Duende.AccessTokenManagement;
5+
6+
/// <summary>
7+
/// Represents a slim version of an HTTP request
8+
/// </summary>
9+
public record struct HttpRequestContext
10+
{
11+
public required string Method { get; init; }
12+
public required Uri? RequestUri { get; init; }
13+
public required IEnumerable<KeyValuePair<string, IEnumerable<string>>> Headers { get; init; }
14+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright (c) Duende Software. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace Duende.AccessTokenManagement;
5+
6+
internal static class HttpRequestContextExtensions
7+
{
8+
public static HttpRequestContext ToHttpRequestContext(this HttpRequestMessage request) =>
9+
new()
10+
{
11+
Method = request.Method.Method,
12+
RequestUri = request.RequestUri,
13+
Headers = request.Headers
14+
};
15+
}

access-token-management/src/AccessTokenManagement/ITokenRequestCustomizer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public interface ITokenRequestCustomizer
1616
/// <param name="cancellationToken">Cancellation token</param>
1717
/// <returns>Customized token request parameters</returns>
1818
Task<TokenRequestParameters> Customize(
19-
HttpRequestMessage httpRequest,
19+
HttpRequestContext httpRequest,
2020
TokenRequestParameters baseParameters,
2121
CancellationToken cancellationToken = default);
2222
}

access-token-management/src/AccessTokenManagement/Internal/ClientCredentialsTokenRetriever.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
namespace Duende.AccessTokenManagement.Internal;
77

8-
98
/// <summary>
109
/// An <see cref="AccessTokenRequestHandler.ITokenRetriever" /> implementation that retrieves a token using the client credentials flow.
1110
/// </summary>
@@ -24,7 +23,7 @@ internal class ClientCredentialsTokenRetriever(
2423
};
2524

2625
var parameters = customizer != null
27-
? await customizer.Customize(request, baseParameters, ct)
26+
? await customizer.Customize(request.ToHttpRequestContext(), baseParameters, ct)
2827
: baseParameters;
2928

3029
var getTokenResult = await clientCredentialsTokenManager.GetAccessTokenAsync(clientName, parameters, ct);

access-token-management/src/AccessTokenManagement/ServiceCollectionExtensions.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -176,17 +176,18 @@ private static IHttpClientBuilder AddClientCredentialsTokenHandlerInternal(
176176
IHttpClientBuilder httpClientBuilder,
177177
Func<IServiceProvider, ITokenRequestCustomizer>? tokenRequestCustomizerFactory,
178178
ClientCredentialsClientName clientName) => httpClientBuilder
179-
.AddHttpMessageHandler(provider =>
180-
{
181-
var accessTokenManagementService = provider.GetRequiredService<IClientCredentialsTokenManager>();
182-
var tokenRequestCustomizer = tokenRequestCustomizerFactory?.Invoke(provider);
183-
var retriever =
184-
new ClientCredentialsTokenRetriever(accessTokenManagementService, clientName,
185-
tokenRequestCustomizer);
186-
var accessTokenHandler = provider.BuildAccessTokenRequestHandler(retriever);
187-
188-
return accessTokenHandler;
189-
});
179+
.AddHttpMessageHandler(provider =>
180+
{
181+
var accessTokenManagementService = provider.GetRequiredService<IClientCredentialsTokenManager>();
182+
var tokenRequestCustomizer = tokenRequestCustomizerFactory?.Invoke(provider) ??
183+
provider.GetService<ITokenRequestCustomizer>();
184+
var retriever =
185+
new ClientCredentialsTokenRetriever(accessTokenManagementService, clientName,
186+
tokenRequestCustomizer);
187+
var accessTokenHandler = provider.BuildAccessTokenRequestHandler(retriever);
188+
189+
return accessTokenHandler;
190+
});
190191

191192
internal static AccessTokenRequestHandler BuildAccessTokenRequestHandler(
192193
this IServiceProvider provider,

0 commit comments

Comments
 (0)