Search code examples
c#asp.net-core.net-6.0

ASP.NET core - simple API key authentication


I am attempting to build a super simple API-key authentication for certain APIs in a controller. For this I have this in ConfigureServices():

services.AddAuthorization(options =>
{
  options.AddPolicy(
    Auth.Constants.WebmasterPolicyName,
    policy =>
      policy.RequireAssertion(context =>
      {
        if (context.Resource is HttpContext httpContext)
        {
          if (httpContext.Request.Headers.TryGetValue("X-API-KEY", out var header))
          {
            var val = header.FirstOrDefault()?.ToLower();
            if (val == "my-super-secret-key")
            {
              return Task.FromResult(true);
            }
          }
        }
        return Task.FromResult(false);
      })
  );
});

I have decorated an API with this:

[HttpDelete("{itemId:guid}")]
[Authorize(Policy = Auth.Constants.WebmasterPolicyName)]
public async Task<ActionResult> DeleteCatalogItemAsync(Guid itemId)

This works perfectly, when I set the correct API key in the request.

The problem is the negative case: When the key is missing or wrong, I will get a 500 error:

System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).
   at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties)
   at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at AlwaysOn.CatalogService.Startup.<>c__DisplayClass5_0.<<Configure>b__3>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)

But I'm not sure what to do with that message. I would just like it to return a 401 response to the client.


Solution

  • We can create a custom ApiKeyMiddleware to implemente simple API key authentication.

    It is somehow similar to what we have done in the custom attribute, but the main difference that you will notice here is that we cannot directly set the Response object of the context but we have to assign the statuscode and message separately.

    Sample Code:

    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using System.Threading.Tasks;
    
    namespace SecuringWebApiUsingApiKey.Middleware
    {
    public class ApiKeyMiddleware
    {
        private readonly RequestDelegate _next;
        private const string APIKEYNAME = "ApiKey";
        public ApiKeyMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            if (!context.Request.Headers.TryGetValue(APIKEYNAME, out var extractedApiKey))
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Api Key was not provided. (Using ApiKeyMiddleware) ");
                return;
            }
    
            var appSettings = context.RequestServices.GetRequiredService<IConfiguration>();
    
            var apiKey = appSettings.GetValue<string>(APIKEYNAME);
    
            if (!apiKey.Equals(extractedApiKey))
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Unauthorized client. (Using ApiKeyMiddleware)");
                return;
            }
    
            await _next(context);
        }
    }
    }
    

    For more details, we can refer this blog.

    Secure ASP.NET Core Web API using API Key Authentication