aspnet / Mvc

[Archived] ASP.NET Core MVC is a model view controller framework for building dynamic web sites with clean separation of concerns, including the merged MVC, Web API, and Web Pages w/ Razor. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
5.62k stars 2.14k forks source link

Return 401 on invalid jwt token instead of login page. #8725

Closed ronaldpostmagnaversum closed 5 years ago

ronaldpostmagnaversum commented 5 years ago

Is this a Bug or Feature request?:

Bug ( or misconfiguration )

Steps to reproduce (preferably a link to a GitHub repo with a repro project):

This is the link to a test project: https://github.com/ronaldpostmagnaversum/razor-pages-mixed-authentication

Description of the problem:

I am having a problem configuring authentication for a razor-pages app that also has an api. I want to have cookie authentication for the razor pages and jwt tokens for the api. The problem is that I can use this mixed authentication and it validates correctly, but when the jwt token is not valid the api returns the login page with a 200 status, instead of a 401. When I turn off the cookie authentication everything works fine. Can somebody tell me if there is something wrong with the configuration or if this is a bug? Thanks in advance.

ConfigureServices:

services.AddMvc(config =>
            {
                var policy = new AuthorizationPolicyBuilder(IdentityConstants.ApplicationScheme,
                                                            JwtBearerDefaults.AuthenticationScheme)
                                 .RequireAuthenticatedUser()
                                 .Build();
                config.Filters.Add(new AuthorizeFilter(policy));
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

Configure:

app.UseMvc(config =>
            {
                var defaultPolicy = new AuthorizationPolicyBuilder(new[]
                        { IdentityConstants.ApplicationScheme,
                          JwtBearerDefaults.AuthenticationScheme })
                    .RequireAuthenticatedUser()
                    .Build();
            });

On the controller I have this attribute:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

Version of Microsoft.AspNetCore.Mvc or Microsoft.AspNetCore.App or Microsoft.AspNetCore.All:

Microsoft.AspNetCore.App 2.1.1

mkArtakMSFT commented 5 years ago

Thanks for contacting us, @ronaldpostmagnaversum. @javiercn, can you please look into this? Thanks!

javiercn commented 5 years ago

@ronaldpostmagnaversum The best way to achieve your scenario is to use an authentication policy provider on top of your cookies and jwt authentication schemes.

@HaoK Can you point him to a sample on how to achieve this?

HaoK commented 5 years ago

Sure take a look at this sample https://github.com/aspnet/AuthSamples/tree/master/samples/PathSchemeSelection which is switching between schemes based on request path as an example

ronaldpostmagnaversum commented 5 years ago

Hi @HaoK and @javiercn,

Thanks for the answers and the reference to the sample project. I fixed it using the ForwardDefaultSelector and tell it to use 'Bearer' AuthHandler on the api path like this:

options.ForwardDefaultSelector = ctx => { return ctx.Request.Path.StartsWithSegments("/api") ? "Bearer" : null; };

I am still wondering if it should work when setting the authentication scheme on the controllers. Is this a bug or does it only work without Razor Pages?

poke commented 5 years ago

Since you likely have the cookie authentication set as the default in order for your mixed authentication setup to work, the cookie authentication handler is responsible of handing the default challenge request and redirects to your login page. You can affect that behavior in a few ways; try one of the solutions mentioned in this Stack Overflow question.

ronaldpostmagnaversum commented 5 years ago

Thanks @poke for the information. I can't seem to get it working trying the solutions on StackOverflow. I will leave it using the "ForwardDefaultSelector" option as this is a sufficient solution for now.

javiercn commented 5 years ago

I believe is because you have both schemes in the global auth filter and one of them is going to fail when sending JWTs

javiercn commented 5 years ago

@haok can confirm. I don’t remember the exact behavior

HaoK commented 5 years ago

Yeah you can't mix cookies and bearer since they do completely different things on challenges, bearer wants to return a 401, and cookies wants to return a 302 to the login page, so only one of them can successfully challenge

javiercn commented 5 years ago

Closing as there's no more action to be taken here