Skip to content
Frank Stüber edited this page Dec 18, 2025 · 6 revisions

Introduction

A .NET library for supporting API key-based access control in ASP.NET Core applications.

  • Supports .NET 10, .NET 9 and .NET 8.
  • Integrates easily into ASP.NET Core using services.AddApiKeyValidation(...).
  • Provides attribute-based protection using [ApiKey(typeof(MyApiKeyPolicyProvider))] to secure controllers or actions.
  • Offers configurable key extraction through Authorization headers (e.g. ApiKey), custom headers (e.g. X-API-KEY), and query parameters.
  • Supports customizable policies that define allowed API keys, IP ranges (CIDR), and local-only access.
  • Includes built-in error handling that returns proper HTTP status codes or RFC 9457 Problem Details responses.
  • Uses an extensible design so you can override or replace extractors, policies, and error factories through dependency injection.

Installation

Add the package reference to your ASP.NET Core project:

dotnet add package Enbrea.ApiKey

Integration

Step 1: Register Services

In your Program.cs or Startup.cs:

using Enbrea.ApiKey;

var builder = WebApplication.CreateBuilder(args);

// Register API key validation
builder.Services.AddApiKeyValidation(options =>
{
    // Optional configuration
    options.AcceptedHeaderNames = new[] { "X-API-KEY" };
    options.AcceptedAuthSchemes = new[] { "ApiKey" };
    options.AcceptedQueryParamNames = new[] { "api_key" };
});

// Optionally switch to ProblemDetails-based responses
builder.Services.AddSingleton<ProblemDetailsFactory, DefaultProblemDetailsFactory>();
builder.Services.AddApiKeyValidation(o => { }).UseProblemDetailsFactory();

var app = builder.Build();
app.MapControllers();
app.Run();

Step 2: Define Your Policy Provider

Implement an IApiKeyPolicyProvider to define the rules for validation.

using Enbrea.ApiKey;

public class MyPolicyProvider : IApiKeyPolicyProvider
{
    public IApiKeyPolicy Get()
    {
        return new ApiKeyPolicy
        {
            Keys = new[] { "secret123", "another-key" },
            AllowLocal = true,
            PrivateOnly = false,
            AllowCidrs = new[] { "10.0.0.0/8" }
        };
    }
}

You can load keys dynamically from a database, configuration file, or remote source if needed.

Step 3: Apply the [ApiKey] Attribute

Decorate controllers or actions that should require an API key:

using Enbrea.ApiKey;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
[ApiKey(typeof(MyPolicyProvider))]
public class DemoController : ControllerBase
{
    [HttpGet("status")]
    public IActionResult GetStatus()
    {
        return Ok(new { status = "OK" });
    }
}

Core Concepts

IApiKeyExtractor

Responsible for reading the API key from the incoming HTTP request.

The extractor can check:

  • The Authorization header with configured schemes (e.g., ApiKey my-secret)
  • Custom headers (e.g., X-API-KEY)
  • Query string parameters (e.g., ?api_key=my-secret)

Example custom configuration:

options.AcceptedHeaderNames = new[] { "X-ACCESS-KEY", "X-API-KEY" };
options.AcceptedQueryParamNames = new[] { "key", "api_key" };

IApiKeyPolicy

Defines the validation rules.

Property Type Description
Keys string[] List of valid API keys.
AllowLocal bool Allows local requests (127.0.0.1 or ::1).
AllowCidrs string[] Allows specific IP ranges (CIDR notation).
PrivateOnly bool Restricts endpoint access to local/private IPs only.

IApiKeyErrorResultFactory

Creates the HTTP responses for invalid or missing keys.

Two implementations are available:

Factory Description
ApiKeyDefaultErrorFactory Returns plain status results (401, 404, 500).
ApiKeyProblemDetailsFactory Returns RFC 9457–compliant ProblemDetails JSON responses.

To enable ProblemDetails:

builder.Services.AddSingleton<ProblemDetailsFactory, DefaultProblemDetailsFactory>();
builder.Services.AddApiKeyValidation(o => { }).UseProblemDetailsFactory();

Examples

Policy Scenarios

Allow local requests and specific API keys:

new ApiKeyPolicy
{
    Keys = new[] { "local123" },
    AllowLocal = true
};

Allow only internal networks:

new ApiKeyPolicy
{
    PrivateOnly = true,
    AllowCidrs = new[] { "192.168.0.0/16", "10.0.0.0/8" }
};

Disable API key for internal use only:

new ApiKeyPolicy
{
    PrivateOnly = true
};

Minimal API Integration

If you want to create a minimal API with ASP.NET Core:

using Enbrea.ApiKey;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddApiKeyValidation(o =>
{
    o.AcceptedHeaderNames = new[] { "X-API-KEY" };
    o.AcceptedAuthSchemes = new[] { "ApiKey" };
});

var app = builder.Build();

app.MapControllers();

app.MapGet("/public", () => "Public endpoint");

app.MapGet("/protected", [ApiKey(typeof(MyPolicyProvider))] () => "Protected endpoint");

app.Run();

Advanced Topics

Custom Extractor

You can implement your own extractor if you use a nonstandard API key format:

public class CustomHeaderExtractor : IApiKeyExtractor
{
    public bool TryGetApiKey(HttpRequest request, out string? apiKey)
    {
        apiKey = request.Headers["X-CUSTOM-HEADER"];
        return !string.IsNullOrEmpty(apiKey);
    }
}

Register it in DI:

services.AddSingleton<IApiKeyExtractor, CustomHeaderExtractor>();

Custom Error Factory

You can override how error responses are created:

public class JsonErrorFactory : IApiKeyErrorResultFactory
{
    public IActionResult Create(HttpContext context, ApiKeyError error)
    {
        return new JsonResult(new { message = "Access denied", code = error.ToString() })
        {
            StatusCode = StatusCodes.Status401Unauthorized
        };
    }
}

Register it in DI:

services.AddSingleton<IApiKeyErrorResultFactory, JsonErrorFactory>();