Client assertions is a method of client authentication which can be used in OpenID Connect. This provides an alternative to client secrets. This approach enhances security by using signed tokens (JWTs) to authenticate clients during the token request process or the OAuth PAR request. In ASP.NET Core, client assertions is not supported per default, a small implementation is required.
Code: https://github.com/damienbod/oidc-client-assertion
Setup
A client assertion can be added to the token request which is sent from the ASP.NET Core backend to the OpenID Connect server. This is sent instead of the a client secret. The client is authenticated using the assertion. If using OAuth, the assertion is sent in the PAR request.

Create client assertion JWT
The application uses Microsoft Entra ID as the OpenID Connect server and the identity provider. The assertion was created using the Microsoft documentation.
A X509Certificate2 is used to create the certificate and validate the certificate. The audience, the clientId and the sub claims are sent in the JWT client assertion as well as the standard claims signed with the private key of the certificate used to validate the assertion in Microsoft Entra ID. As always, if using this against a different OpenID Connect server, JWT will be validated differently. Microsoft Entra ID requires a RSA key size 2048.
public static class CertService
{
public static string GetSignedClientAssertion(
X509Certificate2 certificate, string aud, string clientId)
{
// no need to add exp, nbf as JsonWebTokenHandler will add them by default.
var claims = new Dictionary<string, object>()
{
{ "aud", aud },
{ "iss", clientId },
{ "jti", Guid.NewGuid().ToString() },
{ "sub", clientId }
};
var securityTokenDescriptor = new SecurityTokenDescriptor
{
Claims = claims,
SigningCredentials = new X509SigningCredentials(certificate)
};
var handler = new JsonWebTokenHandler();
var signedClientAssertion = handler.CreateToken(securityTokenDescriptor);
return signedClientAssertion;
}
}
Using Micrsoft Entra ID as the OpenID Connect server, the client assertion is created using the token endpoint. In production, the certificate can be generated using Azure Key Vault. The certificate can also be read from the operating system store. This can all be set in the app.settings.
// single tenant
var aud = $"https://login.microsoftonline.com/{builder.Configuration["AzureAd:TenantId"]!}/oauth2/v2.0/token";
var clientAssertion = CertService.GetSignedClientAssertion(
X509CertificateLoader.LoadPkcs12FromFile("cert_rsa512.pfx", "1234"),
aud,
builder.Configuration["AzureAd:ClientId"]!);
Use the client assertion in the OpenID connect client
Once ready, the OnAuthorizationCodeReceived event can be used to added the assertion in the OpenID Connect client. If using PAR, the par event is used.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, oidcOptions =>
{
oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
oidcOptions.Scope.Add("user.read");
oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
oidcOptions.Authority
= $"https://login.microsoftonline.com/{builder.Configuration["AzureAd:TenantId"]}/v2.0/";
oidcOptions.ClientId = builder.Configuration["AzureAd:ClientId"];
//oidcOptions.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
oidcOptions.MapInboundClaims = false;
oidcOptions.SaveTokens = true;
oidcOptions.TokenValidationParameters.NameClaimType
= JwtRegisteredClaimNames.Name;
oidcOptions.TokenValidationParameters.RoleClaimType = "role";
oidcOptions.Events = new OpenIdConnectEvents
{
// Add client_assertion
OnAuthorizationCodeReceived = context =>
{
context.TokenEndpointRequest!.ClientAssertion = clientAssertion;
context.TokenEndpointRequest.ClientAssertionType
= "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
return Task.FromResult(0);
}
};
});
Setup Entra ID App registration
An Azure App registration is used to authenticate using Micrsoft Entra ID. The public key of the certificate can be uploaded to to the App registration.

Client assertions can be used for any OpenID Connect confidential client if the OpenID Connect server supports this. The assertion can be sent in the token request or in the PAR request depending on the flavor of the OpenID Connect code flow used.
Creating the certificate
The github repository linked at the top, provides an example to create your own certificate which can be used in this flow. You can also use Azure Key vault or any other tool to create the certificate.
Links
https://datatracker.ietf.org/doc/html/rfc7521
https://oauth.net/private-key-jwt/
RFC 7523 – JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants