简体   繁体   中英

Why is the Authentication Cookie not working against the [Authorize] attribute?

We try to implement an Authentication via Cookies in our Blazor-WebAssembly app.

Controller: Set the Auth-Cookie:

[Route("[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
    [HttpPost]
    public async Task<AdUser> Login(Credentials pCredentials)
    {
        // [...] credential check jere

            var lClaims = new List<Claim> {
                new Claim(ClaimTypes.Name, "SamAccountName"),
            };
            var lClaimsIdentity = new ClaimsIdentity(lClaims, CookieAuthenticationDefaults.AuthenticationScheme);

            // set cookie
            await HttpContext.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme,
            new ClaimsPrincipal(lClaimsIdentity),
            new AuthenticationProperties
            {
                IsPersistent = true,
                ExpiresUtc = DateTime.UtcNow.AddYears(1),
                RedirectUri = this.Request.Host.Value
            });

        // [...]
    }
}

When I look into the developer-tools of the edge browser than I can see, that the cookie is set:

在此处输入图像描述

Now the following Controller has a Search-Action and should have restricted access by adding the [Authorize] attribute:

[Route("[controller]")]
[ApiController]
public class ClientsController : ControllerBase
{
    [HttpGet("search")]
    [Authorize]
    public ActionResult<List<Shared.Client>> Search(string pText)
    {
        // [...] Code here
        
        return lResult;
    }
}

When I do an HTTP-Request /Clients?search=My Search Text to the ClientsController, the developer tools of the Edge shows me, that there is a request made to /Account/Login . Thats confusing to me, because the response code is 200 but no Account-Controller exists in my project.

Why is my Authentication Cookie not working against the [Authorize] attribute?

在此处输入图像描述

Some further details about my configurations:

Startup.cs (Server-Side)

namespace BlazorWebAssemblyApp.Server
{
    public class Startup
    {
        /// [...]

        public void ConfigureServices(IServiceCollection services)
        {
            // [...]
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(); // This line is required for the authentication cookie       
            // [...]
        }
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // [...]

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("index.html");
        });
    }
}

If you see that the user is not recognized on future request after you explicitly signed in with the cookie authentication scheme, then that shows that you haven't properly configured the authentication middleware. As per the docs , you will not only have to add the authentication services using services.AddAuthentication(…) but you will also have to configure the authentication middleware to run as part of the request pipeline. This will usually look like this:

app.UseRouting();

// add the call to `UseAuthentication`
app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

By adding the UseAuthentication() call into the middleware, you will cause the default authentication scheme—which is the cookie scheme in your case—to run an attempt to authenticate the user. This makes sure that if there is an authentication cookie in the request, then it will used to authenticate the user, regardless of whether you want to access an authorized route or not.

Once the middleware runs, actions protected with just the [Authorize] attribute will also work since the authentication of the cookie scheme has already happened (since it's the default scheme).

Otherwise, if the middleware is not invoked by default, you will need to make sure that the authentication scheme is always explicitly invoked whenever you need to access the user information. That is what [Authorize(AuthenticationSchemes = "scheme-name")] does: Before the authorization runs, it will attempt to authenticate the specified schemes. – If you use the authentication middleware and have the correct default scheme, then you can skip this since the scheme will be authenticated by default.

In your original code, without the authentication running, this also gives you the explanation why you were being redirected: Since the authentication scheme didn't run to authenticate the user, there was no signed in user (even though the user had a cookie). So when the user was authorized , no user was there and you were being redirected to the login page.

Why is there a redirect to /Account/Login ?

The cookie authentication scheme is the one involved in redirecting users to the login page when authentication is required (eg through the [Authorize] attribute) but the user does not have an authentication cookie yet. In that case, the authentication will be “challenged” which for the cookie scheme means that the user will be redirected to the login page where they should sign in.

By default, the route to the login page is configured to be /Account/Login . This default is there to match the default behavior when you use ASP.NET Core Identity. You can easily configure this route to match your actual login page by changing CookieAuthenticationOptions.LoginPath . You can do that for example with the AddCookie() call:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Auth/Login"; // using the AuthController instead
    });

Now, when users are challenged, they will be redirected to your AuthController.Login action instead where they should sign in.

Note that the cookie scheme will add a request parameter ReturnUrl to the login action which contains the path to the page that the user originally tried to access. Eg when accessing your search action, they would be redirected to /Auth/Login?ReturnUrl=%2FClients%2Fsearch . So you are expected to accept this route parameter and return back to that route when the login is done, eg:

[HttpPost]
public async Task<IActionResult> Login(Credentials pCredentials, string returnUrl)
{
    // do login

    return LocalRedirect(returnUrl);
}

You can also change the name of the parameter ReturnUrl to whatever you like by changing CookieAuthenticationOptions.ReturnUrlParameter .

You're getting redirected to the "Login Page" or returnURL because your authentication isn't working properly and you're getting Unauthorized. ASP.Net Core redirects you by default when it fails to authenticate instead of returning a 401 code.

Make sure that you're implementing it in the right way as stated in https://docs.microsoft.com/es-es/aspnet/core/security/authentication/identity?view=aspnetcore-3.1&tabs=visual-studio .

Don't forget to add the following lines on your Configure method in the Startup.cs class to add the auth middleware:

app.UseAuthentication();
app.UseAuthorization();

Please, check if you've it in the right order.(The order is important, since you first authenticate, and then it checks your role).

They also must be placed between the app.UseRouting() and the app.UseEndpoints() calls.

I got it by my self, using [Authorize(AuthenticationSchemes = AuthSchemes)] fpr my action in the controller. Here is the code:

[Route("[controller]")]
[ApiController]
public class ClientsController : ControllerBase
{          
    private const string AuthSchemes = CookieAuthenticationDefaults.AuthenticationScheme;

    [HttpGet("search")]
    [Authorize(AuthenticationSchemes = AuthSchemes)]
    public ActionResult<List<Shared.Client>> Search(Shared.Client.SearchProperty pProperty, string pText)
    {
        // [...]
    }
}

You can read more about this topic here: https://docs.microsoft.com/de-de/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-3.1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM