Kevin
Kevin

Reputation: 575

Custom AuthenticationHandler is called when a method has [AllowAnonymous]

I am trying to have my own custom authentication for my server. But it is called for every endpoint even if it has the [AllowAnonymous] attribute on the method. With my current code, I can hit my breakpoint in the HandleAuthenticateAsync method everytime, even on the allow anonymous functions.

The AddCustomAuthentication adds the authenticationhandler correctly

        public void ConfigureServices(IServiceCollection services)
        {
            //services.AddAuthorization();
            services.AddAuthentication(options =>
            {
                // the scheme name has to match the value we're going to use in AuthenticationBuilder.AddScheme(...)
                options.DefaultAuthenticateScheme = "scheme";
                options.DefaultChallengeScheme = "scheme";
            })
            .AddCustomAuthentication(o => { });
        }

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

            app.UseAuthentication();

            app.UseMvc();
        }


...

    public class CustomAuthenticationHandler : AuthenticationHandler<CustomAuthenticationOptions>
    {

        public RvxAuthenticationHandler(
        IOptionsMonitor<RvxAuthenticationOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock) : base(options, logger, encoder, clock)
        {
        }


        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            var token = Request.Headers["token"].ToString();

            if (string.IsNullOrWhiteSpace(token))
            {
                return AuthenticateResult.Fail("Invalid Credentials");
            }


            return AuthenticateResult.Success(new AuthenticationTicket(new System.Security.Claims.ClaimsPrincipal(), "Hi"));
        }

Upvotes: 19

Views: 6347

Answers (3)

Alexander Nikolov
Alexander Nikolov

Reputation: 11

I guess Your problem is different, After exiting from the HandleAuthenticateAsync method it can not find the endpoint:

  1. because the address is not correct
  2. or it goes in error before exiting the HandleAuthenticateAsync method. (For example on Request.Headers["Authorization"] as the header "Authorization" does not exists : fires an Exception.). Recommendation: Use Shlager UI to test the Api first.

Upvotes: 0

alexs
alexs

Reputation: 1856

Add this to the top of your HandleAuthenticateAsync method

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var endpoint = Context.GetEndpoint();
        if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
        {
            return Task.FromResult(AuthenticateResult.NoResult());
        }

        ....
    }

This is what Microsoft use under the covers in the AuthorizeFiler - https://github.com/dotnet/aspnetcore/blob/bd65275148abc9b07a3b59797a88d485341152bf/src/Mvc/Mvc.Core/src/Authorization/AuthorizeFilter.cs#L236

It will allow you to use the AllowAnonymous attribute in controllers to bypass your custom AuthenticationHandler.

Upvotes: 25

Michael Domashchenko
Michael Domashchenko

Reputation: 1480

This is how it is designed to work.

Authentication step is executed for every incoming call by the ASP.Net middleware added by your app.UseAuthentication() call. The step only sets up an instance of IPrincipal to the request.

If authentication succeeds, the request gets the IPrincipal that you pass to the AuthenticationTicket.

If it fails, the request gets an unauthenticated IIdentity in its IPrincipal (principal.Identity.IsAuthenticated will be false)

Then the request will still be passed to the next middleware and eventually to your endpoint method.

It's the AuthorizeAttribute that will prevent the request from reaching protected methods, not any AuthenticationHandler<T>.

Upvotes: 11

Related Questions