0

I have a dotnet core v3 web api that uses both Azure AD and Api key authentication. I would like to use bearer tokens on most controller methods but a few require api key access. The bearer token authentication and authorization is working fine. But I am unable to get the api key auth going.

Startup

        public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddMicrosoftIdentityWebApi(Configuration, "AzureAd")
            .EnableTokenAcquisitionToCallDownstreamApi()
            .AddInMemoryTokenCaches();

        services.AddAuthentication()
            .AddScheme<AuthenticationSchemeOptions, ApiKeyAuthenticationHandler>("Api-Key", null);

        services.AddAuthorization(options =>
        {
            options.DefaultPolicy = new AuthorizationPolicyBuilder(
                JwtBearerDefaults.AuthenticationScheme,
                "Api-Key")
            .RequireAuthenticatedUser()
            .Build();

            options.AddPolicy("RequireAdministratorRole",
                 policy => policy.RequireRole("Administrator"));

            options.AddPolicy("RequireApiKeyRole",
                 policy => policy.RequireRole("ApiKeyRole"));
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseAuthentication();
        app.UseAuthorization();

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

The api key authentication handler

protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var authorizationHeaders = Request.Headers["Authorization"];
        var apiKeyHeader = authorizationHeaders.FirstOrDefault(
            header => header.StartsWith(Scheme.Name, StringComparison.OrdinalIgnoreCase));

        string apiKey = apiKeyHeader.Substring(Scheme.Name.Length).Trim();

        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Role, "ApiKeyRole")
        };

        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);

        return Task.FromResult(AuthenticateResult.Success(ticket));
    }

Controller

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    [HttpGet]
    [Authorize(Policy = "RequireApiKeyRole")]
    public ActionResult Get()
    {
        return Ok("Success");
    }
}

This does not work. I am getting authenticated and the role is set. But I get a 403 response.

webdevbing
  • 322
  • 1
  • 3
  • 12
  • Include your controller methods here and all their attributes. You can omit the body, include all the attributes. I have an example of this working. https://stackoverflow.com/questions/49694383/use-multiple-jwt-bearer-authentication/60271047#60271047 – No Refunds No Returns Sep 17 '20 at 13:46
  • @NoRefundsNoReturns I've added to controller method. – webdevbing Sep 18 '20 at 07:28
  • In your authorization header are you trying to use "Api-Key ......" as the value? If so use "Bearer ...." instead. – No Refunds No Returns Sep 19 '20 at 13:12
  • I have 2 schemes, Bearer and Api-Key The problem turned out that I was overwriting the Api-Key ClaimsPrincipal with the bearer claims transformer. – webdevbing Sep 19 '20 at 14:14

0 Answers0