Client Assertions
If your token client is using a client assertion instead of a shared secret, you can provide the assertion in two ways:
- Use the request parameter mechanism to pass a client assertion to the management
- Implement the
IClientAssertionServiceinterface to centralize client assertion creation
Here’s a sample client assertion service using the Microsoft JWT library:
using Duende.AccessTokenManagement;using Duende.IdentityModel;using Duende.IdentityModel.Client;using Microsoft.Extensions.Options;using Microsoft.IdentityModel.JsonWebTokens;using Microsoft.IdentityModel.Tokens;
public class ClientAssertionService(IOptionsSnapshot<ClientCredentialsClient> options) : IClientAssertionService{ public Task<ClientAssertion?> GetClientAssertionAsync( ClientCredentialsClientName? clientName = null, TokenRequestParameters? parameters = null, CancellationToken ct = default) { if (clientName == "invoice") { var options1 = options.Get(clientName);
var descriptor = new SecurityTokenDescriptor { Issuer = options1.ClientId!.ToString(),
// Set the audience to the url of identity server. Do not use the tokenurl to build the autority. Audience = "https://--url-to-authority-here--",
Expires = DateTime.UtcNow.AddMinutes(1), SigningCredentials = GetSigningCredential(),
Claims = new Dictionary<string, object> { { JwtClaimTypes.JwtId, Guid.NewGuid().ToString() }, { JwtClaimTypes.Subject, options1.ClientId.ToString()! }, { JwtClaimTypes.IssuedAt, DateTimeOffset.UtcNow.ToUnixTimeSeconds() } },
AdditionalHeaderClaims = new Dictionary<string, object> { { JwtClaimTypes.TokenType, "client-authentication+jwt" } } };
var handler = new JsonWebTokenHandler(); var jwt = handler.CreateToken(descriptor);
return Task.FromResult<ClientAssertion?>(new ClientAssertion { Type = OidcConstants.ClientAssertionTypes.JwtBearer, Value = jwt }); }
return Task.FromResult<ClientAssertion?>(null); }
private SigningCredentials GetSigningCredential() { throw new NotImplementedException(); }}using Duende.AccessTokenManagement;using Duende.IdentityModel;using Duende.IdentityModel.Client;using Microsoft.Extensions.Options;using Microsoft.IdentityModel.JsonWebTokens;using Microsoft.IdentityModel.Tokens;
public class ClientAssertionService(IOptionsSnapshot<ClientCredentialsClient> options) : IClientAssertionService{ public Task<ClientAssertion?> GetClientAssertionAsync( string? clientName = null, TokenRequestParameters? parameters = null) { if (clientName == "invoice") { var options1 = options.Get(clientName);
var descriptor = new SecurityTokenDescriptor { Issuer = options1.ClientId,
// Set the audience to the url of identity server. Do not use the tokenurl to build the autority. Audience = "https://--url-to-authority-here--", Expires = DateTime.UtcNow.AddMinutes(1), SigningCredentials = GetSigningCredential(),
Claims = new Dictionary<string, object> { { JwtClaimTypes.JwtId, Guid.NewGuid().ToString() }, { JwtClaimTypes.Subject, options1.ClientId! }, { JwtClaimTypes.IssuedAt, DateTime.UtcNow.ToEpochTime() } },
AdditionalHeaderClaims = new Dictionary<string, object> { { JwtClaimTypes.TokenType, "client-authentication+jwt" } } };
var handler = new JsonWebTokenHandler(); var jwt = handler.CreateToken(descriptor);
return Task.FromResult<ClientAssertion?>(new ClientAssertion { Type = OidcConstants.ClientAssertionTypes.JwtBearer, Value = jwt }); }
return Task.FromResult<ClientAssertion?>(null); }
private SigningCredentials GetSigningCredential() { throw new NotImplementedException(); }}For a complete working example, see the WebClientAssertions sample.
Client Assertions With OpenID Connect
Section titled “Client Assertions With OpenID Connect”When using AddOpenIdConnectAccessTokenManagement, the same IClientAssertionService is used to authenticate the client
for the requests Duende.AccessTokenManagement makes itself, such as refreshing and revoking user tokens. The client name
passed to GetClientAssertionAsync is derived from the OpenID Connect scheme name.
Duende.AccessTokenManagement also hooks into the ASP.NET Core OpenID Connect handler events and
automatically adds the client assertion from your IClientAssertionService to the authorization code exchange and, on
.NET 9 or newer, to the pushed authorization request (PAR).
Signed authorization requests (JAR, RFC 9101) are not created by the
library. If your client needs to send a request object, sign it in your own event handler, as shown in
Combining DPoP with custom OpenID Connect events.