Openiddict-core: Using Json Web Tokens with OpenIddict

Created on 1 Aug 2016  路  9Comments  路  Source: openiddict/openiddict-core

Hi,

I am trying to implement JWT in my API, after following through the example in the READ ME and successfully implementing as described, when I add the .UseJsonTokens() method all my resource endpoints result in 401 when trying to make a request with Authorization: Bearer "access_token".

Is there an additional configuration step I am missing?

I have a Angular2 SPA and a few other clients applications that need to request a token from my API and access the resources.

My Startup.cs is as follows:

        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();

            services.AddDbContext<AuthDbContext>(options =>
                options.UseSqlServer(Configuration["ConnectionStrings:Auth"]));

            // Register the Identity services.
            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<AuthDbContext>()
                .AddDefaultTokenProviders();

            // Register the OpenIddict services, including the default Entity Framework stores.
            services.AddOpenIddict<ApplicationUser, AuthDbContext>()
                // Enable the token endpoint (required to use the password flow).
                .EnableTokenEndpoint("/connect/token")

                // Allow client applications to use the grant_type=password flow.
                .AllowPasswordFlow()

                .AllowRefreshTokenFlow()

                // During development, you can disable the HTTPS requirement.
                .DisableHttpsRequirement()

                // Adding this results in 401 on all resources, is there some configuration I am missing?
                //.UseJsonWebTokens()

                // Register a new ephemeral key, that is discarded when the application
                // shuts down. Tokens signed using this key are automatically invalidated.
                // This method should only be used during development.
                .AddEphemeralSigningKey();

            services.AddSwaggerGen(options => SwaggerConfig.ConfigureOptions(options));

            // Application services
        }
 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseStaticFiles();

            //CORS middleware must precede any defined endpoints in app
            app.UseCors(builder =>
            {
                builder.AllowAnyHeader();
                builder.AllowAnyMethod();
                builder.AllowAnyOrigin();
            });

            app.UseOAuthValidation(); // does this need to be replaced with JWT?

            app.UseOpenIddict();

            app.UseMvc();

            app.UseSwagger();
            app.UseSwaggerUi();

            using (var context = new AuthDbContext(
                app.ApplicationServices.GetRequiredService<DbContextOptions<AuthDbContext>>()))
            {
                context.Database.EnsureCreated();
            }
        }

Thanks

question

Most helpful comment

Yep I read that and finally understood what you mean't, thanks.

For anyone else that runs into this issue:

Add the following to configure

            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                Authority = "http://localhost:5000",
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                Audience = "http://localhost:5000",
                RequireHttpsMetadata = false             
            });

When making a request to your token endpoint e.g http://localhost:5000/connect/token make sure the resource parameter is set to the audience you specified in the JwtBearerOptions e.g http://localhost:5000

get_token

Now when making requests to http://localhost:5000/api/**resource** you can just pass the Authorization header with the Bearer token

resource_request

What if the client wants to decode the JWT to read some custom properties on it?

All 9 comments

The OAuth2 validation middleware only works with the default token format. If you really want to use JWT access tokens, you'll have to use the JWT middleware.

OK so I removed app.UseOAuthValidation() and added

            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                Authority = "http://localhost:5000",
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                RequireHttpsMetadata = false                
            });

I still get 401 on all requests passing the Authorization header and token

If you enabled logging, you'd immediately understand what's failing.

Related: https://github.com/openiddict/openiddict-core/issues/180#issuecomment-236398830

Out of curiosity, why not using the default token format?

Yep I did turn on logging but none the wiser, as I am new to implementing OAuth but I gather its something to do with audiences.

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/api/v1.0/sites/1 application/x-www-form-urlencoded
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[1]
      Failed to validate the token eyJhbGciOiJSUzI1NiIsImtpZCI6IlNYQ09aRjBKTk5JWFBJTkUtSUZUQUlHNjZBVVhOUkNKQzFXMVRMMksiLCJ0eXAiOiJKV1QifQ.eyJBc3BOZXQuSWRlbnRpdHkuU2VjdXJpdHlTdGFtcCI6IjcyNzMyMjVhLTVmZGMtNDRiYi1hZjk0LTBhOGFlOWMxMWExNyIsImp0aSI6IjVlMDUxNjc4LWNmNzAtNDc2Ny04ZWFhLThhOWFhOGYxMDJkYyIsInVzYWdlIjoiYWNjZXNzX3Rva2VuIiwic3ViIjoiZjQxYTUzNWItMzZjMC00N2JmLWEzNTAtMzk2OTBiYWRhZTMwIiwibmJmIjoxNDcwMDY2MjcwLCJleHAiOjE0NzAwNjgwNzAsImlhdCI6MTQ3MDA2NjI3MCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwLyJ9.n3vyENnIe786e-0mQrZ4wO_toWkzAhpsyOw70tqdAsgFfgjv9wcluYa3NN-v4WCrkWC3V8K-d215n-2IQPlNPbcQldHFHr9iOl4O5EclVUopD3WvKVbYP2tymYUoEuzYGhy1YLZ9tguoRsb7ol9O3oWbUSLeksDUyJIM9TgnDatJNLbi-OcK6vj7NhKiQQYHEL1fXRwK24J-JPcAmxIYvN09pTvjm8gRW7K4Jo-fzw32NboamcxxrAjNG7pVSJfbXyeAldCdw8JBOJ3KLxfm6uojgCRIp29NUX5NjqEkFzcalFlAS42wQpcT2IgX6iUnbq3C0YNIsIoLdwcIPxyCNA.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'empty'. Did not match: validationParameters.ValidAudience: 'http://localhost:5000/' or validationParameters.ValidAudiences: 'null'.
   at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__1.MoveNext()
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[7]
      Bearer was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'empty'. Did not match: validationParameters.ValidAudience: 'http://localhost:5000/' or validationParameters.ValidAudiences: 'null'.
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
      Authorization failed for user: .
warn: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
      Executing ChallengeResult with authentication schemes ().
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[12]
      AuthenticationScheme: Bearer was challenged.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 172.4352ms 401

I have a client that only accepts JWT tokens at the moment but if I cannot get this working looks like I will have to change it

If you read the link I posted, it will work. You need to set the resource token request parameter.

I have a client that only accepts JWT tokens at the moment but if I cannot get this working looks like I will have to change it

This doesn't make much sense, as client applications are not supposed to read the access token.

Yep I read that and finally understood what you mean't, thanks.

For anyone else that runs into this issue:

Add the following to configure

            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                Authority = "http://localhost:5000",
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                Audience = "http://localhost:5000",
                RequireHttpsMetadata = false             
            });

When making a request to your token endpoint e.g http://localhost:5000/connect/token make sure the resource parameter is set to the audience you specified in the JwtBearerOptions e.g http://localhost:5000

get_token

Now when making requests to http://localhost:5000/api/**resource** you can just pass the Authorization header with the Bearer token

resource_request

What if the client wants to decode the JWT to read some custom properties on it?

What if the client wants to decode the JWT to read some custom properties on it?

It's not supposed to do that (at all). Access tokens are opaque for clients. Instead, consider requesting the openid scope to get an identity token you can use to get the user data you need.

Ah OK understood thanks.

Was this page helpful?
0 / 5 - 0 ratings