Skip to content

Commit 1bd7801

Browse files
Merge pull request #199 from DuendeSoftware/ev/atm/v4
A different take on ATM V4
2 parents 535a854 + 1912309 commit 1bd7801

File tree

190 files changed

+5975
-3791
lines changed

Some content is hidden

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

190 files changed

+5975
-3791
lines changed

Directory.Packages.props

Lines changed: 5 additions & 8 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" />
@@ -57,4 +54,4 @@
5754
<PackageVersion Include="xunit.v3.core" Version="1.0.1" />
5855
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1" />
5956
</ItemGroup>
60-
</Project>
57+
</Project>

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: 3 additions & 4 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
{
@@ -90,7 +89,7 @@
9089

9190
app.MapGet("/ok", () => "ok");
9291

93-
app.MapGet("/token", async (IClientCredentialsTokenManagementService svc, CancellationToken ct) =>
92+
app.MapGet("/token", async (IClientCredentialsTokenManager svc, CancellationToken ct) =>
9493
{
9594
return await svc.GetAccessTokenAsync("c1");
9695
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class CookieEvents : CookieAuthenticationEvents
1515
public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
1616
{
1717
var token = await _store.GetTokenAsync(context.Principal!);
18-
if (token.IsError)
18+
if (!token.Succeeded)
1919
{
2020
context.RejectPrincipal();
2121
}

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: 14 additions & 9 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;
@@ -13,13 +15,13 @@ namespace Web.Controllers;
1315
public class HomeController : Controller
1416
{
1517
private readonly IHttpClientFactory _httpClientFactory;
16-
private readonly IUserTokenManagementService _tokenManagementService;
18+
private readonly IUserTokenManager _tokenManager;
1719
private readonly SampleConfiguration _configuration;
1820

19-
public HomeController(IHttpClientFactory httpClientFactory, IUserTokenManagementService tokenManagementService, IOptions<SampleConfiguration> options)
21+
public HomeController(IHttpClientFactory httpClientFactory, IUserTokenManager tokenManager, IOptions<SampleConfiguration> options)
2022
{
2123
_httpClientFactory = httpClientFactory;
22-
_tokenManagementService = tokenManagementService;
24+
_tokenManager = tokenManager;
2325
_configuration = options.Value;
2426
}
2527

@@ -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+
var token = await _tokenManager.GetAccessTokenAsync(User).GetToken();
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+
var token = await HttpContext.GetUserAccessTokenAsync().GetToken();
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+
var token = await HttpContext.GetClientAccessTokenAsync().GetToken();
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.DPoP;
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 ? ProofKeyString.ParseOrDefault(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: 11 additions & 9 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;
@@ -12,12 +14,12 @@ namespace WebJarJwt.Controllers;
1214
public class HomeController : Controller
1315
{
1416
private readonly IHttpClientFactory _httpClientFactory;
15-
private readonly IUserTokenManagementService _tokenManagementService;
17+
private readonly IUserTokenManager _tokenManager;
1618

17-
public HomeController(IHttpClientFactory httpClientFactory, IUserTokenManagementService tokenManagementService)
19+
public HomeController(IHttpClientFactory httpClientFactory, IUserTokenManager tokenManager)
1820
{
1921
_httpClientFactory = httpClientFactory;
20-
_tokenManagementService = tokenManagementService;
22+
_tokenManager = tokenManager;
2123
}
2224

2325
[AllowAnonymous]
@@ -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+
var token = await _tokenManager.GetAccessTokenAsync(User).GetToken();
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+
var token = await HttpContext.GetUserAccessTokenAsync().GetToken();
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+
var token = await HttpContext.GetClientAccessTokenAsync().GetToken();
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

0 commit comments

Comments
 (0)