OAuth specs access to the keys via 'http://myAuth.com/.well-known/jwks.json'. So you can just use the built in JwtSecurityTokenHandler to validate the token without having to configure the certificate in your application. This is also superior because it accommodates key cycling without redeploying your services.
OWIN Example:
public static class AppBuilderExtensions
{
/// <summary>
/// Use JWT Authentication.
/// </summary>
/// <param name="app">The <see cref="IAppBuilder"/> to use.</param>
/// <param name="audiences">The expected audiences. Will be validated in the token.</param>
/// <returns>The <see cref="IAppBuilder"/> instance.</returns>
public static IAppBuilder UseJwtTokenAuthentication(
this IAppBuilder app,
params string[] audiences)
{
var validationParameters = new TokenValidationParameters
{
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateActor = true,
ValidateIssuerSigningKey = true,
ValidAudiences = audiences,
ValidIssuer = Constants.Issuer,
IssuerSigningKeyResolver = Constants.GetSigningKey,
};
var tokenHandler = new JwtSecurityTokenHandler();
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = audiences,
TokenHandler = tokenHandler,
TokenValidationParameters = validationParameters,
});
return app;
}
}
public static class Constants
{
/// <summary>
/// The authentication issuer.
/// </summary>
public const string Issuer = "https://myAuth.com/";
private static readonly OpenIdConnectKeyResolver KeyResolver;
static Constants() { KeyResolver = new OpenIdConnectKeyResolver(Issuer, TimeSpan.FromHours(1), TimeSpan.FromSeconds(10)); }
/// <summary>
/// Gets the <see cref="IssuerSigningKeyResolver" /> delegate to provide to <see cref="JwtSecurityTokenHandler" />.
/// </summary>
public static IssuerSigningKeyResolver GetSigningKey => KeyResolver.GetSigningKey;
}
Direct call Example:
void ValidateToken(string authenticationToken)
{
var issuer = "https://myAuth.com/";
var audiences = new[]
{
"https://myApplication.com/",
};
using (var signingKeyResolver = new OpenIdConnectKeyResolver(issuer, TimeSpan.Zero, TimeSpan.FromSeconds(10)))
{
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateActor = true,
ValidateIssuerSigningKey = true,
ValidAudiences = audiences,
ValidIssuer = issuer,
IssuerSigningKeyResolver = signingKeyResolver.GetSigningKey,
};
var principal = tokenHandler.ValidateToken(authenticationToken, validationParameters, out var securityToken);
if (principal == null || securityToken == null)
{
throw new UnauthorizedAccessException();
}
}
}
This is baked into Microsoft.IdentityModel.Tokens (see example here: https://github.com/auth0/auth0-aspnet-owin/blob/master/src/Auth0.Owin.OpenIdConnectSigningKeyResolver/OpenIdConnectSigningKeyResolver.cs).
It is a little more work if using System.IdentityModel.Tokens.Jwt (see example here: https://github.com/NaosProject/Naos.Auth/blob/main/Naos.Auth.Recipes.Jwt/OpenIdConnectKeyResolver.cs) I wrote a piece of code to accomplish this which can be copied directly (MIT license) or installed as a mix-in using package 'Naos.Auth.Recipes.Jwt'.