Reputation: 236
I need to implement JWT Bearer Authentication in both a .NET 4.6.1 Web API project, as well as a .NET Core 2.0 web project.
I successfully got the core project up and running with the this sample from Microsoft.
I'm following this sample from Scott Allen for the .NET 4.6.1 project.
My 4.6.1 code looks like this:
public void ConfigureAuth(IAppBuilder app)
{
// Application requuires AD Bearer tokens for access
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://validissuer.blahblah.com/",
ValidAudience = "https://validaudience.blahblah.com"
}
});
}
The same token will correctly validate in the .NET Core API, but not the .NET 4.6.1 API, and I believe I'm missing something minor. Any ideas?
Side question: What are the best practices for what should be validated in a production environment? Both instances attempt to validate the issuer and audience, but should I consider validating anything else?
-Tim
Upvotes: 3
Views: 2582
Reputation: 735
You need to set IssuerSigningKey property. If you don't know how to get the key manually, Here is an example to automatically resovle the IssuerSigningKey by authority:
using System.Linq;
using System.Threading;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Jwt;
public class Startup
{
public void Configuration(IAppBuilder app)
{
var authority = "https://<your-authority>/";
var keyResolver = new OpenIdConnectSigningKeyResolver(authority);
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
AuthenticationType = "Bearer",
ValidIssuer = "https://<your-authority>/",
ValidateAudience = false,
ValidateIssuer = true,
RequireExpirationTime = true,
ValidateLifetime = true,
IssuerSigningKeyResolver = (token, securityToken, kid, parameters) => keyResolver.GetSigningKey(kid)
}
});
}
private class OpenIdConnectSigningKeyResolver
{
private readonly OpenIdConnectConfiguration openIdConfig;
public OpenIdConnectSigningKeyResolver(string authority)
{
var cm = new ConfigurationManager<OpenIdConnectConfiguration>($"{authority.TrimEnd('/')}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
openIdConfig = AsyncHelper.RunSync(async () => await cm.GetConfigurationAsync());
}
public SecurityKey[] GetSigningKey(string kid)
{
// Find the security token which matches the identifier
return new[] { openIdConfig.JsonWebKeySet.GetSigningKeys().FirstOrDefault(t => t.KeyId == kid) };
}
}
private static class AsyncHelper
{
private static readonly TaskFactory TaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static void RunSync(Func<Task> func)
{
TaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return TaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
}
}
Upvotes: 2
Reputation: 61
Since you've assigned new instance of TokenValidationParameters
to the corresponding property, I think, you should also provide a valid IssuerSigningKey
.
builder.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters
{
AuthenticationType = "Bearer",
ValidIssuer = "https://validissuer.blahblah.com/",
ValidAudience = "https://validaudience.blahblah.com",
IssuerSigningKey = new InMemorySymmetricSecurityKey(Encoding.UTF8.GetBytes("MySecret")),
}
});
Upvotes: 1