AspNetCore custom authentication scheme authenticates then fails authorization

Viewed 15

I'm migrating a legacy custom header thingy to IdentityServer client credentials flow.

So that both systems can run simultaneously I want to add a second authentication scheme to my API that supports the legacy systems during a changeover period.

I've created

public class MyProductAuthenticationHandler : AuthenticationHandler<MyProductAuthenticationSchemeOptions>

and hooked it up in Startup.cs with

.AddScheme<MyProductAuthenticationSchemeOptions, MyProductAuthenticationHandler>(MyProductAuthenticationSchemeOptions.AuthenticationScheme, _ => { });

and I can set a breakpoint in its HandleAuthenticateAsync and check that it's succeeding and making an AuthenticationTicket.

However, authorization subsequently fails (it still works for the JWT token authorization). I see these messages in the logging output.

MyProduct.Api.Services.Auth.MyProductAuthenticationHandler: Debug: AuthenticationScheme: MyProductwas successfully authenticated.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Debug: AuthenticationScheme: Bearer was not authenticated.
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Information: Authorization failed.
MyProduct.Api.Services.Auth.MyProductAuthenticationHandler: Information: AuthenticationScheme: MyProduct was forbidden.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: AuthenticationScheme: Bearer was forbidden.
MyProduct.Middleware.LoggingMiddleware: Warning: 403 @ GET /api/global/manage/tenants/TenantNameAvailable

The problem seems to be that

AuthenticationScheme: MyProduct was forbidden

but why is it forbidden after it authorized?

My policy is

                options.AddPolicy(MyProductAuthorizationPolicy.GlobalTenantManagement, policy =>
                {
                    policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
                    policy.AuthenticationSchemes.Add(MyProductAuthenticationSchemeOptions.AuthenticationScheme); // Obsolete. Remove after switching to client credentials flow.
                    policy.RequireAuthenticatedUser();
                    policy.RequireClaim("client_id", "client");
                    policy.RequireClaim("my_product_management", "global");
                });

If I change my policy to

                    policy.AddAuthenticationSchemes(TruthLiteAuthenticationSchemeOptions.AuthenticationScheme);
                    policy.RequireAuthenticatedUser(); // If absent then there are no claims below.
                    policy.RequireAssertion((context) => {
                        int c = context.User.Claims.Count();
                        return true;
                    });

then then HandleAuthenticateAsync runs and I get the cliams, but

AuthenticationScheme: MyProduct was forbidden.

happens again and 403 is returned.

1 Answers

Having found Get a 403 when using dual authorization (Bearer & Basic) in .net core

I guessed that the answer was to pass the scheme name as a parameter to the constructor of ClaimsIdentity in my HandleAuthenticateAsync, i.e.,

ClaimsIdentity identity = new ClaimsIdentity(Scheme.Name);

instead of

ClaimsIdentity identity = new ClaimsIdentity();
Related