2

I am tying to do JwtBearerAuthentication on my .net WebAPI and it is just not working. The Authorize attribute is always claiming isAuthorized = false.

I am working with Okta as the SSO. I am authenticating on my client side and getting both an access token and id token. On a webapi get request I am providing the access token (i have also tried the id token) in the authorize header and I am able to see the authorize header with the token in the webapi actioncontext.

In my startup.cs I have the following

var clientID = WebConfigurationManager.AppSettings["okta:ClientId"];

var oidcIssuer = WebConfigurationManager.AppSettings["okta:OIDC_Issuer"];

TokenValidationParameters tvps = new TokenValidationParameters
{
    ValidAudience = clientID,
    ValidateAudience = true,
    ValidIssuer = oidcIssuer,
    ValidateIssuer = true  
};

app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
    TokenValidationParameters = tvps,
    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
    {
        new OpenIdConnectCachingSecurityTokenProvider(oidcIssuer + "/.well-known/openid-configuration")
    }
});

Am i missing some TokenValidationParameters that I need?

twaldron
  • 2,722
  • 7
  • 40
  • 55

2 Answers2

2

As I was typing this, I see that twaldron was able to figure it out!

I also realized that he was asking about WebAPI, and not MVC. However, here is the code that I needed to get the following working with ASP.NET Core MVC, of particular interest might be this line, which is necessary to get access to the additional claims in the JWT:

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

Here is how this code sample works from the command line, the $ID_TOKEN variable contains a valid JWT:

$ curl -H "Authorization: Bearer ${ID_TOKEN}" http://localhost:3000/test/test
sub: 01a23b4cd5eFgHI6j7k8 email:test@example.com

Setup.cs:

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;

namespace WebApplication
{
    public class Startup
    {
        readonly string clientId = string.Empty;
        readonly string issuer = string.Empty;
        readonly string audience = string.Empty;

        public Startup(IHostingEnvironment env)
        {
            clientId = "A0b1CDef2GHIj3k4lm5n";
            issuer = "https://example.okta.com";
            audience = "A0b1CDef2GHIj3k4lm5n";
        }

        public IConfigurationRoot Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddDebug();

            // https://github.com/aspnet/Security/issues/1043#issuecomment-261937401
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

            TokenValidationParameters tvps = new TokenValidationParameters
            {

                ValidateAudience = true,
                ValidAudience = audience,

                ValidateIssuer = true,
                ValidIssuer = issuer,

                ValidateLifetime = true,
                ClockSkew = TimeSpan.FromMinutes(5)
            };

            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                MetadataAddress = issuer + "/.well-known/openid-configuration",
                TokenValidationParameters = tvps
            });


            app.UseStaticFiles();

            // Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "test-controller",
                    template: "test/{action}",
                    defaults: new { controller = "Test", action = "Index" }
                );
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Test}/{action=Index}/{id?}");
            });
        }
    }
}

In Controllers/Test.cs:

    [Authorize]
    public IActionResult Test()
    {
        var contextUser = User.Identity as ClaimsIdentity;
        Dictionary<string, string> claim = contextUser.Claims.ToDictionary(x => x.Type, x => x.Value);

        var output = "sub: " + claim["sub"] + " email:" + claim["email"];
        return Content(output);
    }
Joël Franusic
  • 1,178
  • 8
  • 18
  • Thanks for the tip about the groups claims. This is actually what I am struggling with now. I am not getting them. JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); didn't work for me actualIy I didn't have that exact method, I could only find InboundClaimTypeMap.Clear(); – twaldron Feb 14 '17 at 17:04
  • 1
    I got this code working using "Microsoft.AspNetCore.Mvc": "1.0.1" on "Microsoft.NETCore.App": "1.1.0". I had to add these to my project.json file to get things working: Microsoft.AspNetCore.Authentication.OAuth, Microsoft.AspNetCore.Authentication.JwtBearer, Microsoft.AspNetCore.Identity - I can't remember which one defines JwtSecurityTokenHandler. Let me know if you find out! – Joël Franusic Feb 14 '17 at 20:53
1

My problem was not with the options. It was 100% the need to move

app.UseWebApi(config);

below all the owin setup stuff.

twaldron
  • 2,722
  • 7
  • 40
  • 55
  • 1
    Glad you were able to figure this out! I added some code below, only because I had issues getting access to claims and thought that might be helpful to you? (You should mark this as "answered"?) – Joël Franusic Feb 11 '17 at 00:36