Skip to content

Commit 436c900

Browse files
first stab at v4
1 parent 535a854 commit 436c900

File tree

184 files changed

+5677
-3616
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

184 files changed

+5677
-3616
lines changed

Directory.Packages.props

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,22 @@
22
<PropertyGroup>
33
<WilsonVersion>7.1.2</WilsonVersion>
44
</PropertyGroup>
5-
65
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
76
<ExtensionsVersion>8.0.0</ExtensionsVersion>
87
<WilsonVersion>7.1.2</WilsonVersion>
98
</PropertyGroup>
10-
119
<PropertyGroup Condition=" '$(TargetFramework)' == 'net8.0'">
1210
<FrameworkVersion>8.0.1</FrameworkVersion>
1311
<ExtensionsVersion>9.0.3</ExtensionsVersion>
14-
<WilsonVersion>[8.0.1,9.0.0)</WilsonVersion>
12+
<WilsonVersion>[8.0.1,9.0.0)</WilsonVersion>
1513
<IdentityServerVersion>7.0.8</IdentityServerVersion>
1614
</PropertyGroup>
17-
1815
<PropertyGroup Condition=" '$(TargetFramework)' == 'net9.0'">
1916
<FrameworkVersion>9.0.0</FrameworkVersion>
2017
<ExtensionsVersion>9.0.3</ExtensionsVersion>
2118
<WilsonVersion>[8.0.1,9.0.0)</WilsonVersion>
2219
<IdentityServerVersion>7.0.8</IdentityServerVersion>
2320
</PropertyGroup>
24-
2521
<ItemGroup>
2622
<PackageVersion Include="AngleSharp" Version="1.1.2" />
2723
<PackageVersion Include="BullsEye" Version="5.0.0" />
@@ -36,14 +32,15 @@
3632
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid " Version="9.3.0" />
3733
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(ExtensionsVersion)" />
3834
<PackageVersion Include="Microsoft.Extensions.Http" Version="$(ExtensionsVersion)" />
35+
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.0.0" />
3936
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(ExtensionsVersion)" />
4037
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(ExtensionsVersion)" />
4138
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="$(ExtensionsVersion)" />
4239
<PackageVersion Include="Microsoft.Extensions.Telemetry.Abstractions" Version="9.1.0" />
4340
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="$(WilsonVersion)" />
44-
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
41+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
4542
<PackageVersion Include="Microsoft.NETCore.Jit" Version="2.0.8" />
46-
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0"/>
43+
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
4744
<PackageVersion Include="MinVer" Version="6.0.0" />
4845
<PackageVersion Include="PublicApiGenerator" Version="11.1.0" />
4946
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0" />
@@ -54,6 +51,7 @@
5451
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
5552
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
5653
<PackageVersion Include="Verify.XunitV3" Version="28.9.0" />
54+
<PackageVersion Include="Vogen" Version="7.0.3" />
5755
<PackageVersion Include="xunit.v3.core" Version="1.0.1" />
5856
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1" />
5957
</ItemGroup>

access-token-management/perf/Perf.DevServer.ServiceDefaults/Extensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
using System.Collections;
55
using System.Runtime.CompilerServices;
6-
using Duende.AccessTokenManagement;
6+
using Duende.AccessTokenManagement.OTel;
77
using Microsoft.AspNetCore.Builder;
88
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
99
using Microsoft.Extensions.DependencyInjection;

access-token-management/perf/Perf.TokenEndpoint/Program.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,11 @@
3737
services.AddClientCredentialsTokenManagement(opt => opt.CacheLifetimeBuffer = 0)
3838
.AddClient("c1", opt =>
3939
{
40-
opt.TokenEndpoint = new Uri(Services.IdentityServer.ActualUri(), "/connect/token").ToString();
40+
opt.TokenEndpoint = new Uri(Services.IdentityServer.ActualUri(), "/connect/token");
4141
opt.ClientId = "tokenendpoint";
4242
opt.ClientSecret = "secret";
4343
opt.HttpClientName = "c1";
44-
})
45-
.UsePreviewHybridCache();
44+
});
4645

4746
builder.Services.AddStackExchangeRedisCache(options =>
4847
{

access-token-management/samples/BlazorServer/Plumbing/OidcEvents.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ public override async Task TokenValidated(TokenValidatedContext context)
2222
AccessTokenType = context.TokenEndpointResponse.TokenType,
2323
Expiration = exp,
2424
RefreshToken = context.TokenEndpointResponse.RefreshToken,
25-
Scope = context.TokenEndpointResponse.Scope
25+
Scope = context.TokenEndpointResponse.Scope,
26+
ClientId = context.ProtocolMessage.ClientId,
27+
IdentityToken = context.TokenEndpointResponse.IdToken
2628
});
2729

2830
await base.TokenValidated(context);

access-token-management/samples/BlazorServer/Plumbing/ServerSideTokenStore.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
using System.Collections.Concurrent;
55
using System.Security.Claims;
6+
using Duende.AccessTokenManagement;
67
using Duende.AccessTokenManagement.OpenIdConnect;
78

9+
810
namespace BlazorServer.Plumbing;
911

1012
/// <summary>
@@ -13,29 +15,33 @@ namespace BlazorServer.Plumbing;
1315
/// </summary>
1416
public class ServerSideTokenStore : IUserTokenStore
1517
{
16-
private static readonly ConcurrentDictionary<string, UserToken> _tokens = new();
18+
private static readonly ConcurrentDictionary<string, TokenForParameters> _tokens = new();
1719

18-
public Task<UserToken> GetTokenAsync(ClaimsPrincipal user, UserTokenRequestParameters? parameters = null)
20+
public Task<TokenResult<TokenForParameters>> GetTokenAsync(ClaimsPrincipal user, UserTokenRequestParameters? parameters = null,
21+
CancellationToken cancellationToken = default)
1922
{
2023
var sub = user.FindFirst("sub")?.Value ?? throw new InvalidOperationException("no sub claim");
2124

2225
if (_tokens.TryGetValue(sub, out var value))
2326
{
24-
return Task.FromResult(value);
27+
return Task.FromResult(TokenResult.Success(value));
2528
}
2629

27-
return Task.FromResult(new UserToken { Error = "not found" });
30+
return Task.FromResult((TokenResult<TokenForParameters>)TokenResult.Failure("not found"));
2831
}
2932

30-
public Task StoreTokenAsync(ClaimsPrincipal user, UserToken token, UserTokenRequestParameters? parameters = null)
33+
public Task StoreTokenAsync(ClaimsPrincipal user, UserToken token, UserTokenRequestParameters? parameters = null, CancellationToken ct = default)
3134
{
3235
var sub = user.FindFirst("sub")?.Value ?? throw new InvalidOperationException("no sub claim");
33-
_tokens[sub] = token;
36+
_tokens[sub] = new TokenForParameters(token,
37+
token.RefreshToken == null
38+
? null
39+
: new UserRefreshToken(token.RefreshToken.Value, token.DPoPJsonWebKey));
3440

3541
return Task.CompletedTask;
3642
}
3743

38-
public Task ClearTokenAsync(ClaimsPrincipal user, UserTokenRequestParameters? parameters = null)
44+
public Task ClearTokenAsync(ClaimsPrincipal user, UserTokenRequestParameters? parameters = null, CancellationToken ct = default)
3945
{
4046
var sub = user.FindFirst("sub")?.Value ?? throw new InvalidOperationException("no sub claim");
4147

access-token-management/samples/Web/Controllers/HomeController.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
33

44
using System.Text.Json;
5+
using Duende.AccessTokenManagement;
56
using Duende.AccessTokenManagement.OpenIdConnect;
7+
68
using Duende.IdentityModel.Client;
79
using Microsoft.AspNetCore.Authorization;
810
using Microsoft.AspNetCore.Mvc;
@@ -32,9 +34,9 @@ public HomeController(IHttpClientFactory httpClientFactory, IUserTokenManagement
3234

3335
public async Task<IActionResult> CallApiAsUserManual()
3436
{
35-
var token = await _tokenManagementService.GetAccessTokenAsync(User);
37+
UserToken token = await _tokenManagementService.GetAccessTokenAsync(User);
3638
var client = _httpClientFactory.CreateClient();
37-
client.SetToken(token.AccessTokenType!, token.AccessToken!);
39+
client.SetToken(token.AccessTokenType?.ToScheme().ToString()!, token.AccessToken.ToString());
3840

3941
var response = await client.GetStringAsync($"{_configuration.ApiBaseUrl}test");
4042
ViewBag.Json = PrettyPrint(response);
@@ -44,9 +46,11 @@ public async Task<IActionResult> CallApiAsUserManual()
4446

4547
public async Task<IActionResult> CallApiAsUserExtensionMethod()
4648
{
47-
var token = await HttpContext.GetUserAccessTokenAsync();
49+
UserToken token = await HttpContext.GetUserAccessTokenAsync();
4850
var client = _httpClientFactory.CreateClient();
49-
client.SetToken(token.AccessTokenType!, token.AccessToken!);
51+
var scheme = token.AccessTokenType?.ToScheme().ToString()
52+
?? throw new InvalidOperationException("Scheme is empty");
53+
client.SetToken(scheme, token.AccessToken.ToString());
5054

5155
var response = await client.GetStringAsync($"{_configuration.ApiBaseUrl}test");
5256
ViewBag.Json = PrettyPrint(response);
@@ -86,9 +90,10 @@ public async Task<IActionResult> CallApiAsUserResourceIndicator()
8690
[AllowAnonymous]
8791
public async Task<IActionResult> CallApiAsClientExtensionMethod()
8892
{
89-
var token = await HttpContext.GetClientAccessTokenAsync();
93+
ClientCredentialsToken token = await HttpContext.GetClientAccessTokenAsync();
9094
var client = _httpClientFactory.CreateClient();
91-
client.SetToken(token.AccessTokenType!, token.AccessToken!);
95+
var scheme = token.AccessTokenType?.ToScheme().ToString() ?? throw new InvalidOperationException("Scheme is empty");
96+
client.SetToken(scheme, token.AccessToken.ToString());
9297

9398
var response = await client.GetStringAsync($"{_configuration.ApiBaseUrl}test");
9499

access-token-management/samples/Web/Startup.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
using System.Security.Cryptography;
55
using System.Text.Json;
6+
using Duende.AccessTokenManagement;
67
using Duende.AccessTokenManagement.OpenIdConnect;
8+
79
using Microsoft.IdentityModel.Tokens;
810
using Serilog;
911
using Serilog.Events;
@@ -72,12 +74,12 @@ internal static WebApplication ConfigureServices(this WebApplicationBuilder buil
7274
var rsaKey = new RsaSecurityKey(RSA.Create(2048));
7375
var jsonWebKey = JsonWebKeyConverter.ConvertFromRSASecurityKey(rsaKey);
7476
jsonWebKey.Alg = "PS256";
75-
var jwk = JsonSerializer.Serialize(jsonWebKey);
77+
var jwk = JsonSerializer.Serialize(jsonWebKey) ?? throw new InvalidOperationException("Failed to deserialize");
7678

7779
builder.Services.AddOpenIdConnectAccessTokenManagement(options =>
7880
{
7981
var useDPoP = builder.Configuration.GetValue<bool>("UseDPoP");
80-
options.DPoPJsonWebKey = useDPoP ? jwk : null;
82+
options.DPoPJsonWebKey = useDPoP ? DPoPJsonWebKey.Parse(jwk) : null;
8183
});
8284

8385
// registers HTTP client that uses the managed user access token

access-token-management/samples/WebJarJwt/ClientAssertionService.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using Duende.AccessTokenManagement;
55
using Duende.AccessTokenManagement.OpenIdConnect;
6+
67
using Duende.IdentityModel;
78
using Duende.IdentityModel.Client;
89
using Microsoft.IdentityModel.JsonWebTokens;
@@ -36,22 +37,23 @@ public class ClientAssertionService : IClientAssertionService
3637
public ClientAssertionService(
3738
IOpenIdConnectConfigurationService configurationService) => _configurationService = configurationService;
3839

39-
public async Task<ClientAssertion?> GetClientAssertionAsync(string? clientName = null,
40-
TokenRequestParameters? parameters = null)
40+
public async Task<ClientAssertion?> GetClientAssertionAsync(ClientName? clientName = null,
41+
TokenRequestParameters? parameters = null,
42+
CancellationToken ct = default)
4143
{
42-
var config = await _configurationService.GetOpenIdConnectConfigurationAsync();
44+
var config = await _configurationService.GetOpenIdConnectConfigurationAsync(ct: ct);
4345

4446
var descriptor = new SecurityTokenDescriptor
4547
{
46-
Issuer = config.ClientId,
47-
Audience = config.Authority,
48+
Issuer = config.ClientId.ToString(),
49+
Audience = config.TokenEndpoint.GetLeftPart(UriPartial.Authority),
4850
Expires = DateTime.UtcNow.AddMinutes(1),
4951
SigningCredentials = Credential,
5052

5153
Claims = new Dictionary<string, object>
5254
{
5355
{ JwtClaimTypes.JwtId, Guid.NewGuid().ToString() },
54-
{ JwtClaimTypes.Subject, config.ClientId! },
56+
{ JwtClaimTypes.Subject, config.ClientId.ToString()! },
5557
{ JwtClaimTypes.IssuedAt, DateTime.UtcNow.ToEpochTime() }
5658
},
5759

@@ -71,7 +73,8 @@ public ClientAssertionService(
7173
};
7274
}
7375

74-
public async Task<string> SignAuthorizeRequest(OpenIdConnectMessage message)
76+
public async Task<string> SignAuthorizeRequest(OpenIdConnectMessage message,
77+
CancellationToken ct = default)
7578
{
7679
var config = await _configurationService.GetOpenIdConnectConfigurationAsync();
7780

@@ -83,8 +86,8 @@ public async Task<string> SignAuthorizeRequest(OpenIdConnectMessage message)
8386

8487
var descriptor = new SecurityTokenDescriptor
8588
{
86-
Issuer = config.ClientId,
87-
Audience = config.Authority,
89+
Issuer = config.ClientId.ToString(),
90+
Audience = config.TokenEndpoint.GetLeftPart(UriPartial.Authority),
8891
Expires = DateTime.UtcNow.AddMinutes(1),
8992
SigningCredentials = Credential,
9093

access-token-management/samples/WebJarJwt/Controllers/HomeController.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
33

44
using System.Text.Json;
5+
using Duende.AccessTokenManagement;
56
using Duende.AccessTokenManagement.OpenIdConnect;
7+
68
using Duende.IdentityModel.Client;
79
using Microsoft.AspNetCore.Authorization;
810
using Microsoft.AspNetCore.Mvc;
@@ -29,9 +31,9 @@ public HomeController(IHttpClientFactory httpClientFactory, IUserTokenManagement
2931

3032
public async Task<IActionResult> CallApiAsUserManual()
3133
{
32-
var token = await _tokenManagementService.GetAccessTokenAsync(User);
34+
UserToken token = await _tokenManagementService.GetAccessTokenAsync(User);
3335
var client = _httpClientFactory.CreateClient();
34-
client.SetBearerToken(token.AccessToken!);
36+
client.SetBearerToken(token.AccessToken.ToString()!);
3537

3638
var response = await client.GetStringAsync("https://demo.duendesoftware.com/api/test");
3739
ViewBag.Json = PrettyPrint(response);
@@ -41,9 +43,9 @@ public async Task<IActionResult> CallApiAsUserManual()
4143

4244
public async Task<IActionResult> CallApiAsUserExtensionMethod()
4345
{
44-
var token = await HttpContext.GetUserAccessTokenAsync();
46+
UserToken token = await HttpContext.GetUserAccessTokenAsync();
4547
var client = _httpClientFactory.CreateClient();
46-
client.SetBearerToken(token.AccessToken!);
48+
client.SetBearerToken(token.AccessToken.ToString());
4749

4850
var response = await client.GetStringAsync("https://demo.duendesoftware.com/api/test");
4951
ViewBag.Json = PrettyPrint(response);
@@ -72,9 +74,9 @@ public async Task<IActionResult> CallApiAsUserFactoryTyped([FromServices] TypedU
7274
[AllowAnonymous]
7375
public async Task<IActionResult> CallApiAsClientExtensionMethod()
7476
{
75-
var token = await HttpContext.GetClientAccessTokenAsync();
77+
ClientCredentialsToken token = await HttpContext.GetClientAccessTokenAsync();
7678
var client = _httpClientFactory.CreateClient();
77-
client.SetBearerToken(token.AccessToken!);
79+
client.SetBearerToken(token.AccessToken.ToString());
7880

7981
var response = await client.GetStringAsync("https://demo.duendesoftware.com/api/test");
8082

access-token-management/samples/Worker/ClientAssertionService.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
33

44
using Duende.AccessTokenManagement;
5+
56
using Duende.IdentityModel;
67
using Duende.IdentityModel.Client;
78
using Microsoft.Extensions.Options;
@@ -10,10 +11,8 @@
1011

1112
namespace WorkerService;
1213

13-
public class ClientAssertionService : IClientAssertionService
14+
public class ClientAssertionService(IOptionsMonitor<ClientCredentialsClient> options) : IClientAssertionService
1415
{
15-
private readonly IOptionsMonitor<ClientCredentialsClient> _options;
16-
1716
private static string RsaKey =
1817
"""
1918
{
@@ -32,25 +31,23 @@ public class ClientAssertionService : IClientAssertionService
3231

3332
private static SigningCredentials Credential = new(new JsonWebKey(RsaKey), "RS256");
3433

35-
public ClientAssertionService(IOptionsMonitor<ClientCredentialsClient> options) => _options = options;
36-
37-
public Task<ClientAssertion?> GetClientAssertionAsync(string? clientName = null, TokenRequestParameters? parameters = null)
34+
public Task<ClientAssertion?> GetClientAssertionAsync(ClientName? clientName, TokenRequestParameters? parameters = null, CancellationToken ct = default)
3835
{
3936
if (clientName == "demo.jwt")
4037
{
41-
var options = _options.Get(clientName);
38+
var options1 = options.Get(clientName.ToString());
4239

4340
var descriptor = new SecurityTokenDescriptor
4441
{
45-
Issuer = options.ClientId,
46-
Audience = options.TokenEndpoint,
42+
Issuer = options1.ClientId?.ToString(),
43+
Audience = options1.TokenEndpoint?.ToString(),
4744
Expires = DateTime.UtcNow.AddMinutes(1),
4845
SigningCredentials = Credential,
4946

5047
Claims = new Dictionary<string, object>
5148
{
5249
{ JwtClaimTypes.JwtId, Guid.NewGuid().ToString() },
53-
{ JwtClaimTypes.Subject, options.ClientId! },
50+
{ JwtClaimTypes.Subject, options1.ClientId?.ToString()! },
5451
{ JwtClaimTypes.IssuedAt, DateTime.UtcNow.ToEpochTime() }
5552
}
5653
};

access-token-management/samples/Worker/Program.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static IHostBuilder CreateHostBuilder(string[] args)
3434
services.AddClientCredentialsTokenManagement()
3535
.AddClient("demo", client =>
3636
{
37-
client.TokenEndpoint = "https://demo.duendesoftware.com/connect/token";
37+
client.TokenEndpoint = new Uri("https://demo.duendesoftware.com/connect/token");
3838

3939
client.ClientId = "m2m.short";
4040
client.ClientSecret = "secret";
@@ -43,7 +43,7 @@ public static IHostBuilder CreateHostBuilder(string[] args)
4343
})
4444
.AddClient("demo.dpop", client =>
4545
{
46-
client.TokenEndpoint = "https://demo.duendesoftware.com/connect/token";
46+
client.TokenEndpoint = new Uri("https://demo.duendesoftware.com/connect/token");
4747
//client.TokenEndpoint = "https://localhost:5001/connect/token";
4848

4949
client.ClientId = "m2m.dpop";
@@ -55,7 +55,7 @@ public static IHostBuilder CreateHostBuilder(string[] args)
5555
})
5656
.AddClient("demo.jwt", client =>
5757
{
58-
client.TokenEndpoint = "https://demo.duendesoftware.com/connect/token";
58+
client.TokenEndpoint = new Uri("https://demo.duendesoftware.com/connect/token");
5959
client.ClientId = "m2m.short.jwt";
6060

6161
client.Scope = "api";

0 commit comments

Comments
 (0)