Building an ASP.NET Core Web API with API Key Authentication

by admin
API

In today’s digital landscape, securing web APIs is of utmost importance to protect sensitive data and ensure authorized access. One common method of securing APIs is through the use of API keys, which are unique identifiers assigned to clients for authentication purposes. In this article, we will explore how to implement API key authentication in an ASP.NET Core Web API.

Setting Up API Key Authentication

To begin, let’s create a custom authentication handler that will handle API key authentication. Here’s an example implementation of the ApiKeyAuthenticationHandler:

public class ApiKeyAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private const string API_KEY_HEADER = "Authorization";

    private readonly DbContext _context;

    public ApiKeyAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock,
        DbContext context
    ) : base(options, logger, encoder, clock)
    {
        _context = context;
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (!Request.Headers.ContainsKey(API_KEY_HEADER))
        {
            return AuthenticateResult.Fail("Header not found");
        }

        string apiKeyToValidate = Request.Headers[API_KEY_HEADER];

        var user = await _context
            .Users
            .SingleOrDefaultAsync(uak => uak.ApiKey == apiKeyToValidate);

        if (user == null)
            return AuthenticateResult.Fail("Invalid key");

        return AuthenticateResult.Success(CreateTicket(user));
    }

    private AuthenticationTicket CreateTicket(User user)
    {
        var identity = new ClaimsIdentity(new[]
            {
                new Claim(type: ClaimTypes.NameIdentifier, value: user.Id.ToString())
            }, Scheme.Name);

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

        return ticket;
    }
}

Let’s break down the key components of this code:

  1. The ApiKeyAuthenticationHandler class is derived from AuthenticationHandler<AuthenticationSchemeOptions>, which provides the base functionality for authentication handlers in ASP.NET Core.
  2. We define a constant API_KEY_HEADER that represents the header key where the API key will be sent. In this example, we assume it’s named “Authorization,” but you can modify it to match your specific implementation.
  3. The constructor of ApiKeyAuthenticationHandler injects the required dependencies, including an instance of the DbContext, which represents the database context.
  4. The HandleAuthenticateAsync method is the core of the authentication process. It verifies the presence of the API key in the request header and retrieves the corresponding user from the database based on the provided key.
  5. If the API key is not found or the user is not valid, the method returns an AuthenticateResult indicating failure.
  6. If the user is valid, the CreateTicket method is called to create an AuthenticationTicket object, which encapsulates the authenticated user and other relevant information.
  7. The CreateTicket method constructs a ClaimsIdentity object containing claims associated with the authenticated user. In this example, we include a NameIdentifier claim with the user’s ID.
  8. Using the ClaimsIdentity, we create a ClaimsPrincipal object representing the authenticated user.
  9. Finally, we create an AuthenticationTicket object, passing in the ClaimsPrincipal and the authentication scheme name, and return it.

In your Program.cs, register the authentication scheme:

builder.Services
.AddAuthentication("ApiKey")
.AddScheme<AuthenticationSchemeOptions, ApiKeyAuthenticationHandler>("ApiKey", options => { });

The role of AuthenticationTicket

The AuthenticationTicket plays a crucial role in the ASP.NET Core authentication pipeline. It encapsulates the authenticated user’s identity, associated claims, and other relevant information, allowing the framework to maintain the user’s authentication state throughout the request pipeline.

When the HandleAuthenticateAsync method successfully authenticates a user, it creates an AuthenticationTicket using the CreateTicket method. This ticket is then passed back to the authentication middleware, which uses it to establish the user’s authenticated context.

By encapsulating the authenticated user and associated claims within the AuthenticationTicket, ASP.NET Core can conveniently access and utilize this information at various stages of request processing.

Related Posts

Leave a Comment