Cirreum.Authorization.ApiKey 1.0.6

There is a newer version of this package available.
See the version list below for details.
dotnet add package Cirreum.Authorization.ApiKey --version 1.0.6
                    
NuGet\Install-Package Cirreum.Authorization.ApiKey -Version 1.0.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Cirreum.Authorization.ApiKey" Version="1.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Cirreum.Authorization.ApiKey" Version="1.0.6" />
                    
Directory.Packages.props
<PackageReference Include="Cirreum.Authorization.ApiKey" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Cirreum.Authorization.ApiKey --version 1.0.6
                    
#r "nuget: Cirreum.Authorization.ApiKey, 1.0.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Cirreum.Authorization.ApiKey@1.0.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Cirreum.Authorization.ApiKey&version=1.0.6
                    
Install as a Cake Addin
#tool nuget:?package=Cirreum.Authorization.ApiKey&version=1.0.6
                    
Install as a Cake Tool

Cirreum Authorization Provider - API Key

NuGet Version NuGet Downloads GitHub Release License .NET

API key authentication provider for the Cirreum Framework

Overview

Cirreum.AuthorizationProvider.ApiKey provides header-based API key authentication for ASP.NET Core applications within the Cirreum ecosystem. It enables secure service-to-service communication and broker authentication scenarios where OAuth/OIDC flows are not appropriate.

Key Features

  • Header-based authentication - Validates API keys from configurable HTTP headers
  • Multi-client support - Multiple clients can share the same header with unique keys
  • Secure validation - Constant-time comparison prevents timing attacks
  • Role-based authorization - Configure roles per client for fine-grained access control
  • Seamless integration - Works alongside audience-based providers (Entra, Okta) in the same application
Dynamic API Key Resolution (NEW)

For scenarios with hundreds of partners/customers, use database-backed dynamic resolution:

  • Efficient database lookup - Use X-Client-Id header to query only relevant keys
  • Built-in caching - Configurable TTL to reduce database load
  • Composite resolvers - Chain config-based and database-backed resolvers
  • Extensible base class - Implement DynamicApiKeyClientResolver for custom storage

Use Cases

  • Service-to-service communication
  • Broker applications pushing data to APIs
  • External system integrations
  • Background job authentication
  • IoT device connectivity

Installation

dotnet add package Cirreum.AuthorizationProvider.ApiKey

Configuration

Add API key clients to your appsettings.json:

{
  "Cirreum": {
    "Authorization": {
      "Providers": {
        "ApiKey": {
          "Instances": {
            "TrackBroker": {
              "Enabled": true,
              "HeaderName": "X-Api-Key",
              "ClientId": "track-broker",
              "ClientName": "Track Broker Application",
              "Key": "your-secure-api-key-here",
              "Roles": ["App.System"]
            },
            "ExternalService": {
              "Enabled": true,
              "HeaderName": "X-Api-Key",
              "ClientId": "external-service",
              "ClientName": "External Integration Service",
              "Key": "different-secure-key",
              "Roles": ["App.Agent"]
            }
          }
        }
      }
    }
  }
}

Configuration Properties

Property Required Description
Enabled Yes Whether this client is active
HeaderName Yes HTTP header name to check (default: X-Api-Key)
ClientId Yes Unique identifier for the client, used in claims
ClientName No Display name for the client (defaults to ClientId)
Key No* The API key value (*or provide via ConnectionStrings:{InstanceName})
Roles No Roles to assign to authenticated requests

Secure Key Storage

API keys can be provided in two ways (checked in order):

  1. Direct value - Key property in instance configuration (dev/testing only)
  2. Connection string - ConnectionStrings:{InstanceName} in configuration (production)

For production environments, store API keys in Azure Key Vault using the connection string pattern:

{
  "ConnectionStrings": {
    "LapCastBroker": "@Microsoft.KeyVault(SecretUri=https://your-vault.vault.azure.net/secrets/LapCastBrokerKey)"
  },
  "Cirreum": {
    "Authorization": {
      "Providers": {
        "ApiKey": {
          "Instances": {
            "LapCastBroker": {
              "Enabled": true,
              "HeaderName": "X-Api-Key",
              "ClientId": "lapcast-broker",
              "ClientName": "LapCast Broker",
              "Roles": ["App.System"]
            }
          }
        }
      }
    }
  }
}

The instance name (LapCastBroker) is used as the connection string key, allowing both the API and client applications to resolve the same secret from Key Vault using configuration.GetConnectionString("LapCastBroker").

For local development, use user secrets or direct configuration:

{
  "Cirreum": {
    "Authorization": {
      "Providers": {
        "ApiKey": {
          "Instances": {
            "LapCastBroker": {
              "Enabled": true,
              "HeaderName": "X-Api-Key",
              "ClientId": "lapcast-broker",
              "Key": "dev-only-key"
            }
          }
        }
      }
    }
  }
}

Generating API Keys

For secure key generation, use RandomNumberGenerator:

var key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));

Architecture

The provider follows the Cirreum header-based authorization pattern:

ApiKeyAuthorizationRegistrar
└── Extends HeaderAuthorizationProviderRegistrar
    ├── Registers clients in ApiKeyClientRegistry
    ├── Registers scheme via AuthorizationSchemeRegistry.RegisterHeaderScheme()
    └── Configures ApiKeyAuthenticationHandler

ApiKeyAuthenticationHandler
├── Extracts key from configured header
├── Validates against ApiKeyClientRegistry
├── Builds ClaimsPrincipal with ClientId, ClientName, Roles
└── Uses constant-time comparison for security

ApiKeyAuthorizationInstanceSettings
└── Extends HeaderAuthorizationProviderInstanceSettings
    └── HeaderName, ClientId, ClientName, Roles

Authentication Flow

  1. Request arrives with API key header (e.g., X-Api-Key: your-key)
  2. ForwardDefaultSelector detects header and routes to API key scheme
  3. ApiKeyAuthenticationHandler extracts the key value
  4. ApiKeyClientRegistry.ValidateKey() performs secure comparison against all registered clients for that header
  5. On match, handler builds ClaimsPrincipal with client identity and roles
  6. Authorization policies evaluate roles as normal

Multi-Client Support

Multiple clients can use the same header name with different keys:

{
  "ApiKey": {
    "Instances": {
      "ClientA": {
        "HeaderName": "X-Api-Key",
        "ClientId": "client-a",
        "Key": "key-for-client-a",
        "Roles": ["App.Admin"]
      },
      "ClientB": {
        "HeaderName": "X-Api-Key",
        "ClientId": "client-b",
        "Key": "key-for-client-b",
        "Roles": ["App.User"]
      }
    }
  }
}

The ApiKeyClientRegistry validates the provided key against all clients registered for that header and returns the matching client, enabling different roles and identities per key.

Dynamic API Key Resolution

For large-scale deployments with many partners/customers, implement database-backed resolution:

Basic Setup

builder.AddAuthorization(auth => auth
    .AddDynamicApiKeys<DatabaseApiKeyResolver>(
        headers: ["X-Api-Key"],
        options => options.WithCaching())
);

Implementing a Custom Resolver

public class DatabaseApiKeyResolver : DynamicApiKeyClientResolver {
    private readonly IDbConnection _db;

    public DatabaseApiKeyResolver(
        IApiKeyValidator validator,
        IOptions<ApiKeyValidationOptions> options,
        IDbConnection db,
        ILogger<DatabaseApiKeyResolver> logger)
        : base(validator, options, logger) {
        _db = db;
    }

    public override IReadOnlySet<string> SupportedHeaders =>
        new HashSet<string> { "X-Api-Key" };

    protected override async Task<IEnumerable<StoredApiKey>> LookupKeysAsync(
        string headerName,
        ApiKeyLookupContext context,
        CancellationToken cancellationToken) {

        // Use X-Client-Id header for efficient database lookup
        var clientId = context.GetHeader("X-Client-Id");
        if (!string.IsNullOrEmpty(clientId)) {
            return await _db.QueryAsync<StoredApiKey>(
                "SELECT * FROM ApiKeys WHERE ClientId = @ClientId AND IsActive = 1",
                new { ClientId = clientId });
        }

        // Fallback: return all keys for the header (less efficient)
        return await _db.QueryAsync<StoredApiKey>(
            "SELECT * FROM ApiKeys WHERE HeaderName = @HeaderName AND IsActive = 1",
            new { HeaderName = headerName });
    }
}

With Caching

builder.AddAuthorization(auth => auth
    .AddDynamicApiKeys<DatabaseApiKeyResolver>(
        headers: ["X-Api-Key"],
        options => options.WithCaching(caching => {
            caching.DefaultExpiration = TimeSpan.FromMinutes(5);
            caching.SlidingExpiration = TimeSpan.FromMinutes(1);
        }))
);

Composite Resolution (Config + Database)

Static keys from appsettings are automatically checked first, then the dynamic resolver:

// Config keys (appsettings.json) + Database keys - automatic composite
builder.AddAuthorization(auth => auth
    .AddDynamicApiKeys<DatabaseApiKeyResolver>(
        headers: ["X-Api-Key"],
        options => options.WithCaching())
);

Security Considerations

  • Constant-time comparison - Key validation uses CryptographicOperations.FixedTimeEquals to prevent timing attacks
  • Key storage - Never commit API keys to source control; use Azure Key Vault or similar secret management
  • Key rotation - Plan for key rotation by supporting multiple active keys during transition periods
  • Transport security - Always use HTTPS to protect keys in transit
  • Least privilege - Assign minimum required roles to each client

Claims

Authenticated requests receive the following claims:

Claim Value
ClaimTypes.NameIdentifier ClientId
ClaimTypes.Name ClientName
ClaimTypes.Role Each configured role
client_type api_key
auth_scheme The scheme name

License

This project is licensed under the MIT License - see the LICENSE file for details.


Cirreum Foundation Framework
Layered simplicity for modern .NET

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Cirreum.Authorization.ApiKey:

Package Downloads
Cirreum.Runtime.Authorization

The Runtime Authorization configuration.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.19 121 5/7/2026
1.0.18 116 5/1/2026
1.0.17 125 4/28/2026
1.0.16 124 4/26/2026
1.0.15 124 4/14/2026
1.0.14 189 3/17/2026
1.0.12 115 3/17/2026
1.0.11 135 3/13/2026
1.0.10 104 3/12/2026
1.0.9 127 3/9/2026
1.0.8 105 3/6/2026
1.0.7 182 1/30/2026
1.0.6 137 1/21/2026
1.0.5 291 12/20/2025
1.0.4 132 12/20/2025
1.0.3 211 12/20/2025
1.0.1 323 12/16/2025
1.0.0 212 11/29/2025