ASP.NET Core 2.0 JWT Validation fails with `Authorization failed for user: (null)` error
Asked Answered
S

11

53

I'm using ASP.NET Core 2.0 application (Web API) as a JWT issuer to generate a token consumable by a mobile app. Unfortunately, this token couldn't be validated by one controller while can be validated by another (using the same validation setting within the same asp.net core 2.0 app).

So I have a token which is valid and could be decoded, has all the required claims and timestamps. But one endpoint accepts it, while another gives me 401 error and debug output:

Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed for user: (null).

[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
      Authorization failed for user: (null).
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed for user: (null).
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
      Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes ().
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
      Executing ChallengeResult with authentication schemes ().
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
      AuthenticationScheme: Bearer was challenged.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: AuthenticationScheme: Bearer was challenged.
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action MyController.Get (WebApi) in 72.105ms
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action MyController.Get (WebApi) in 72.105ms
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 271.077ms 401 
[40m[32minfo[39m[22m[49m: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 271.077ms 401 

My validation setup is below:

var secretKey = Configuration["Authentication:OAuth:IssuerSigningKey"];
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
var tokenValidationParameters = new TokenValidationParameters
{
   ValidateIssuerSigningKey = true,
   IssuerSigningKey = signingKey,
   ValidateIssuer = true,
   ValidIssuer = Configuration["Authentication:OAuth:Issuer"],
   ValidateAudience = true,
   ValidAudience = Configuration["Authentication:OAuth:Audience"],
   ValidateLifetime = true,
   ClockSkew = TimeSpan.Zero,
};

services.AddAuthentication(options =>
{
   options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
   options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
   options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.RequireHttpsMetadata = false;
    options.TokenValidationParameters = tokenValidationParameters;
});

These two endpoints are identical, just live in different controllers, both marked with the Authorize attribute.

How is that possible?

Stephen answered 22/8, 2017 at 1:56 Comment(1)
+1 Your detailed code drew my attention to the ClockSkew thing, allowing me to understand a separate issue whereby my tokens didn't seem to be expiring.Nurmi
P
124

The sequence of the add statements in the configure function is of importance. Make sure that

app.UseAuthentication();

comes before

app.UseMvc();

Might this have been the problem?

Pejsach answered 23/10, 2017 at 13:17 Comment(5)
In my case, the initial request to an operation decorated with the Authorize attribute would succeed, then any subsequent requests would fail. This fixed my issue.Isacco
I am embarrassed to say that I was missing app.UseAuthentication(); entirely!Hendrickson
Forget about the order, I totally forgot to put the app.UseAthentication() part.Thanks, Ole.Purdum
I knew that order mattered but I still didn't think that Authentication had to come before MVC....duh. Thank you!Sweatbox
Can someone please explain why the order matters.Fraxinella
E
29

For Dotnetcore 3.1, I placed app.UseAuthentication() before app.UseAuthorization()

Endow answered 16/3, 2020 at 8:38 Comment(1)
If i could give this a 1000 upvotes i would. If there is such a need for an order to these things, you'd think there'd be a warningAtrip
P
15

In your startup.cs ConfigureServices method if you add

services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options => ...

Explanation: When you use [Authorize] on a controller it binds to the first authorization system by default.

options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;

With this you are setting your default to JWT Bearer authentication.

additionally you can add

options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

this line is how to prevent getting 404 not found errors when using Identity with JWTs. If you are using identity the DefaultChallengeScheme will try to redirect you to a login page, which if non existent will result in getting a 404 not found rather than the wanted 401 unauthorized. by setting the DefaultChallengeScheme to JwtBearerDefaults.AuthenticationScheme on unauthorized it will no longer try to redirect you to a login page

If you are using Cookie Authentication with JWT authentication in the [Authorize] tag you can specify what authenticationScheme you want. for example

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Paine answered 20/2, 2018 at 17:10 Comment(1)
can you explain what is happening here: stackoverflow.com/questions/51263883 . Mine is working only with [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]Dotterel
D
10

For anyone using .NET Core 3.1, this is how it works:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseCors("AllowOrigin");
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}
Drawn answered 17/3, 2020 at 17:26 Comment(0)
B
8

I added:

app.UseAuthentication();

In Startup.Configure() and that resolved this error for me.

Reference: Auth 2.0 Migration announcement

Beagle answered 20/10, 2017 at 15:57 Comment(0)
T
3

When Authentications are added like:

  services.AddAuthentication(options => {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        ....

It means that every attribute [Authorize] that is put on top of a method or a controller class, will try to authenticate against the default authentication schema (in this case the JwtBearer) AND IT WILL NOT CASCADE DOWN to try to authenticate with other schemas that might be declared (like Cookie schema). In order to make the AuthorizeAttribute authenticate against the cookie schema it has to be specified like

[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]

This will work also the other way around, i.e. if cookie schema is default then the JwtBearer schema must be declared for authorization for those methods or controllers that would need JwtBearer token authentication

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Taradiddle answered 7/11, 2018 at 19:17 Comment(0)
D
2

try this in startup.cs

services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(opts => ...
Deirdredeism answered 10/10, 2017 at 5:30 Comment(0)
B
2

This seems to be the behavior you receive when your JWT isn't validated correctly. I had this problem as a result of typing "Bearer: (JWT)" instead of "Bearer (JWT)" in the header

Buster answered 10/2, 2018 at 21:17 Comment(1)
Thanks. I was using swagger to test my endpoints and was facing this auth error. Your comment led me on the right path to sort out my swagger definitions so that the token is correctly passed. (In my case, it was missing "Bearer")Floats
S
2

In case anyone has no luck with answers involving the configuration of IServiceCollection.AddAuthentication or the implementation of Startup.Configure, one thing you may try is to change the configuration of IServiceCollection.AddAuthorization.

Before making this change, calls to API with a proper token failed with the following log lines.

[Information] [Microsoft.AspNetCore.Hosting.Diagnostics] Request starting HTTP/1.1 POST http://localhost:5000/api application/json 18
[Debug] [Microsoft.AspNetCore.Routing.Matching.DfaMatcher] 1 candidate(s) found for the request path '"/api"'
[Debug] [Microsoft.AspNetCore.Routing.Matching.DfaMatcher] Endpoint '"ApplicationNamespace.Controllers.ApiController.CreateAsync (ApplicationNamespace)"' with route pattern '"Api"' is valid for the request path '"/api"'
[Debug] [Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware] Request matched endpoint '"ApplicationNamespace.Controllers.ApiController.CreateAsync (ApplicationNamespace)"'
[Debug] [Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler] AuthenticationScheme: "Identity.Application" was not authenticated.
[Debug] [Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler] AuthenticationScheme: "Identity.Application" was not authenticated.
[Information] [Microsoft.AspNetCore.Authorization.DefaultAuthorizationService] Authorization failed.
[Information] [Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler] Successfully validated the token.
[Information] [Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler] AuthenticationScheme: "Bearer" was challenged.
[Information] [Serilog.AspNetCore.RequestLoggingMiddleware] HTTP "POST" "/api" responded 401 in 4.6311 ms

In Startup.ConfigureServices, it works for me once I applied a default policy with authentication scheme specified.

services.AddAuthorization(opt =>
  {
    var builder = new AuthorizationPolicyBuilder();
    builder.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
    builder.RequireAuthenticatedUser();
    opt.DefaultPolicy = builder.Build();
  });
Sandrasandro answered 30/10, 2020 at 11:49 Comment(0)
B
0

Check signing key encoding in your token provider it can be for example UTF8 not ASCII.

Bobsledding answered 22/12, 2018 at 17:47 Comment(0)
F
-1

You can try this instead:

.AddJwtBearer(options =>
{
    options.RequireHttpsMetadata = false;
    options.TokenValidationParameters = tokenValidationParameters;
});'
Faulk answered 22/8, 2017 at 6:49 Comment(2)
Pretty sure that will not work. As of core 2.0, "AutomaticAuthenticate" and "AutomaticChallenge" is removed from JwtBearerOptions. read here: learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/…Myriagram
yep, it was removed, and now it's replaced with defaultAuthenticateScheme and defaultChallengeScheme.Stephen

© 2022 - 2024 — McMap. All rights reserved.