简体   繁体   中英

Custom ASP.NET Core Cookie Authentication

I've spent the last few days toying with authentication for my service in ASP.NET Core. My app has a simple authentication token system. It is expected that the request has a cookie on it, I take that cookie and make a request to my auth server. The auth server gives me back the user's entitlements. If the cookie doesn't exist, or the auth request comes back with a failure, the app should spit out a 401. On a Success it will go to the next part of the pipeline and check authorization on the entitlements.

I set up my authentication middleware as one would expect - inheriting from the AuthenticationHandler, AuthenticationMiddleware, etc. My custom Authentication Handler inherits from Authenticationhandler and overwrites HandleAuthenticateAsync(). This method uses the user supplied cookie to get user data, create my ClaimsPrincipal, and either returns AuthenticateResult.Success or AuthenticateResult.Fail.

When AuthenticationResult.Fail returns, I figured the app would quit, but my app will still go to the next part of the pipeline (app.UseMvc()), when I thought it would return a 401 error.

My Startup.cs looks like follows.

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication();
        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        app.UseCustomAuthentication(new CustomAuthenticationOptions()
        {
            AutomaticChallenge = true,
            AutomaticAuthenticate = true
        });

        app.UseMvc();
    }
}

This will fail authentication, I'll see it in the output, but then UseMvc will still run. It isn't until I did this to services that it would quit, but with an authorization error rather than the authentication error that should have been flagged.

        services.AddMvc(config =>
        {
            var policy = new AuthorizationPolicyBuilder()
                             .RequireAuthenticatedUser()
                             .Build();
            config.Filters.Add(new AuthorizeFilter(policy));
        });

Is this how it is supposed to be set up? When authentication fails, shouldn't the pipeline shut down?

When authentication fails, shouldn't the pipeline shut down?

Suppose you have another authentication middleware:

    app.UseCustomAuthentication(new CustomAuthenticationOptions()
    {
        AutomaticChallenge = true,
        AutomaticAuthenticate = true
    });
    app.UseOtherAuthentication(new OtherAuthenticationOptions()
    {
        AutomaticChallenge = true,
        AutomaticAuthenticate = true
    });

If pipeline ends when first authentication fails, other authentication middleware never doesn't run. It would be less extensible.

Another point, suppose you want to allow some action for anonymous request with using AllowAnonymous attribute. How do you allow?

Even if an authentication middleware fails, without calling HttpContext.Authentication.ChallengeAsync() or using Authorize attribute, server doesn't respond 401, 403 or 302.

As far as i know it is expected behaviour and built-in cookie authentication works the same behaviour. If you want to force authenticated user firstly, you need to add it globally(as you do) or use Authorize attribute on top of controller.

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