Tim_Cardwell
Tim_Cardwell

Reputation: 236

Owin/Katana UseJwtBearerAuthentication Always Returns 401

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

Answers (2)

capcom923
capcom923

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

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

Related Questions