How to add ApiKey schema validation as the second validation schema to a WebApi in .NET Core

1.1k Views Asked by At

I have a Web API that as already JwtBearer schema validation in place. Now I need to add ApiKey schema validation to the Web API to be used by a service.

I follow this guide, create the ApiKeyAttribute class and decorated the controller method with [ApiKey] attribute.

Still, when I call the Web API method, I get the 401 error.

Here is how the call is made:

//add "Authentication: Bearer somekey" header to every http call
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("ApiKey", "somekey"); 

The method OnActionExecutionAsync of ApiKeyAttribute class is never called.

I try to add the Authentication schema like this to the Web API startup with no success at all:

builder.Services    
 .AddControllers().Services
 .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 
 ...
 .AddAuthentication("ApiKey").Services //the second schema, ApiKey
 .Add...

Instead, it returns

No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action configureOptions).

Any clues?

Update 25-04-2002

For the sake of completeness, i add here the code of all relevant parts. It works fine with Bearer scheme before i try to add ApiKey scheme at start.

The attribute class:

[AttributeUsage(validOn: AttributeTargets.Class | AttributeTargets.Method)]
public class ApiKeyAttribute : Attribute, IAsyncActionFilter
{
    private const string APIKEYNAME = "ApiKey";
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        Console.WriteLine("ApiKeyAttribute  HIT !!!"); //not passing here
        if (!context.HttpContext.Request.Headers.TryGetValue(APIKEYNAME, out var extractedApiKey))
        {
            context.Result = new ContentResult()
            {
                StatusCode = 401,
                Content = "Api Key was not provided"
            };
            return;
        }

        var appSettings = context.HttpContext.RequestServices.GetRequiredService<IConfiguration>();

        var apiKey = appSettings.GetValue<string>(APIKEYNAME);

        if (!apiKey.Equals("TESTE"))
        {
            context.Result = new ContentResult()
            {
                StatusCode = 401,
                Content = "Api Key is not valid"
            };
            return;
        }

        await next();
    }
}

The controller with the decorated method with ApiKey:

[ApiController]
[Route("[controller]")]

public class SomeController : ControllerBase
{
...

    [HttpGet()]
    [Produces("application/json")]
    //[Authorize(Roles = Roles.Read)] removed temporarily so i can put ApiKey scheme before i enable again the Bearer scheme that allows Role authorization. It is desirable to enable both schemes on same methods. 
    [ApiKey]
    public List<Rule> foo() {
      ...
    }
}

Program:

 var builder = WebApplication.CreateBuilder(args);
 ... //configs
 builder.Services    
 .AddControllers().Services
 .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 
 ...
 .AddAuthentication("ApiKey").Services //the second schema, ApiKey
 .Add...

var app = builder.Build();
...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller}/{action=Index}/{id?}");

app.MapFallbackToFile("index.html"); ;
app.Run();

Update 26-04-2026

Using the Attribute class don't seem to be the way to implement ApiKey schema validation, or at least i failed to put it to work.

I change the ApiKey schema validation implementation to the one posted here. Works well as the only schema validation. Still need to find out how to add it as the second validation schema.

1

There are 1 best solutions below

3
On BEST ANSWER

Set Authentication Scheme to the controller explicitly.

[ApiController]
[Route("[controller]")]
[Authorize(AuthenticationSchemes = "ApiKey")]
public class SomeController : ControllerBase
{
    // ACTIONS
}

To set the second Authentication Scheme, You can use the below approach:

[ApiController]
[Route("[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme + "," + apiKey.ApiKeyAuthenticationDefaults.AuthenticationScheme)]
public class SomeController : ControllerBase
{
    // ACTIONS
}