Building an API Key Management System with ABP Framework
API keys are one of the most common authentication methods for APIs, especially for machine-to-machine communication. In this article, I'll explain what API key authentication is, when to use it, and how to implement a complete API key management system using ABP Framework.
What is API Key Authentication?
An API key is a unique identifier used to authenticate requests to an API. Unlike user credentials (username/password) or OAuth tokens, API keys are designed for:
- Programmatic access - Scripts, CLI tools, and automated processes
- Service-to-service communication - Microservices authenticating with each other
- Third-party integrations - External systems accessing your API
- IoT devices - Embedded systems with limited authentication capabilities
- Mobile/Desktop apps - Native applications that need persistent authentication
Why Use API Keys?
While modern authentication methods like OAuth2 and JWT are excellent for user authentication, API keys offer distinct advantages in certain scenarios:
Simplicity: No complex OAuth flows or token refresh mechanisms. Just include the key in your request header.
Long-lived: Unlike JWT tokens that expire in minutes/hours, API keys can remain valid for months or years, making them ideal for automated systems.
Revocable: You can instantly revoke a compromised key without affecting user credentials.
Granular Control: Different keys for different purposes (read-only, admin, specific services).
Real-World Use Cases
Here are some practical scenarios where API key authentication shines:
1. Mobile Applications
Your mobile app needs to call your backend APIs. Instead of storing user credentials or managing token refresh flows, use an API key.
// Mobile app configuration
var apiClient = new ApiClient("https://api.yourapp.com");
apiClient.SetApiKey("sk_mobile_prod_abc123...");
2. Microservice Communication
Service A needs to call Service B's protected endpoints.
// Order Service calling Inventory Service
var request = new HttpRequestMessage(HttpMethod.Get, "https://inventory-service/api/products");
request.Headers.Add("X-Api-Key", _configuration["InventoryService:ApiKey"]);
3. Third-Party Integrations
You're providing APIs to external partners or customers.
curl -H "X-Api-Key: pk_partner_xyz789..." \
https://api.yourplatform.com/api/orders
Implementing API Key Management in ABP Framework
Now let's see how to build a complete API key management system using ABP Framework. I've created an open-source implementation that you can use in your projects.
Project Overview
The implementation consists of:
- User-based API keys - Each key belongs to a specific user
- Permission delegation - Keys inherit user permissions with optional restrictions
- Secure storage - Keys are hashed with SHA-256
- Prefix-based lookup - Fast key resolution with caching
- Web UI - Manage keys through a user-friendly interface
- Multi-tenancy support - Full ABP multi-tenancy compatibility
Architecture Overview
The solution follows ABP's modular architecture with four main layers:
┌─────────────────────────────────────────────┐
│ Web Layer (UI) │
│ • Razor Pages for CRUD operations │
│ • JavaScript for client interactions │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ AspNetCore Layer (Middleware) │
│ • Authentication Handler │
│ • API Key Resolver (Header/Query) │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Application Layer (Business Logic) │
│ • ApiKeyAppService (CRUD operations) │
│ • DTO mappings and validations │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Domain Layer (Core Business) │
│ • ApiKey Entity & Manager │
│ • IApiKeyRepository │
│ • Domain services & events │
└─────────────────────────────────────────────┘
Key Components
1. Domain Layer - The Core Entity
public class ApiKey : FullAuditedAggregateRoot<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid UserId { get; protected set; }
public virtual string Name { get; protected set; }
public virtual string Prefix { get; protected set; }
public virtual string KeyHash { get; protected set; }
public virtual DateTime? ExpiresAt { get; protected set; }
public virtual bool IsActive { get; protected set; }
// Key format: {prefix}_{key}
// Only the hash is stored, never the actual key
}
Key Design Decisions:
- Prefix-based lookup: Keys have format
prefix_actualkey. The prefix is indexed for fast database lookups. - SHA-256 hashing: The actual key is hashed and never stored in plain text.
- User association: Each key belongs to a user, inheriting their permissions.
- Soft delete: Deleted keys are marked as deleted but not removed from database for audit purposes.
2. Authentication Flow
Here's how authentication works when a request arrives:
// 1. Extract API key from request
var apiKey = httpContext.Request.Headers["X-Api-Key"].FirstOrDefault();
if (string.IsNullOrEmpty(apiKey)) return AuthenticateResult.NoResult();
// 2. Split prefix and key
var parts = apiKey.Split('_', 2);
var prefix = parts[0];
var key = parts[1];
// 3. Find key by prefix (cached)
var apiKeyEntity = await _apiKeyRepository.FindByPrefixAsync(prefix);
if (apiKeyEntity == null) return AuthenticateResult.Fail("Invalid API key");
// 4. Verify hash
var keyHash = HashHelper.ComputeSha256(key);
if (apiKeyEntity.KeyHash != keyHash)
return AuthenticateResult.Fail("Invalid API key");
// 5. Check expiration and active status
if (apiKeyEntity.ExpiresAt < DateTime.UtcNow || !apiKeyEntity.IsActive)
return AuthenticateResult.Fail("API key expired or inactive");
// 6. Create claims principal with user identity
var claims = new List<Claim>
{
new Claim(AbpClaimTypes.UserId, apiKeyEntity.UserId.ToString()),
new Claim(AbpClaimTypes.TenantId, apiKeyEntity.TenantId?.ToString() ?? ""),
new Claim("ApiKeyId", apiKeyEntity.Id.ToString())
};
return AuthenticateResult.Success(ticket);
3. Creating and Managing API Keys
Creating a new key:
public class ApiKeyManager : DomainService
{
public async Task<(ApiKey, string)> CreateAsync(
Guid userId,
string name,
DateTime? expiresAt = null)
{
// Generate unique prefix
var prefix = await GenerateUniquePrefixAsync();
// Generate secure random key
var key = GenerateSecureRandomString(32);
// Hash the key for storage
var keyHash = HashHelper.ComputeSha256(key);
var apiKey = new ApiKey(
GuidGenerator.Create(),
userId,
name,
prefix,
keyHash,
expiresAt,
CurrentTenant.Id
);
await _apiKeyRepository.InsertAsync(apiKey);
// Return both entity and the full key (prefix_key)
// This is the ONLY time the actual key is visible
return (apiKey, $"{prefix}_{key}");
}
}
Important: The actual key is returned only once during creation. After that, only the hash is stored.
Using API Keys in Your Application
Once created, clients can use the API key to authenticate:
HTTP Header (Recommended):
curl -H "X-Api-Key: sk_prod_abc123def456..." \
https://api.example.com/api/products
JavaScript:
const response = await fetch('https://api.example.com/api/products', {
headers: {
'X-Api-Key': 'sk_prod_abc123def456...'
}
});
C# HttpClient:
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Api-Key", "sk_prod_abc123def456...");
var response = await client.GetAsync("https://api.example.com/api/products");
Python:
import requests
headers = {'X-Api-Key': 'sk_prod_abc123def456...'}
response = requests.get('https://api.example.com/api/products', headers=headers)
Permission Management
API keys inherit the user's permissions, but you can further restrict them:
This allows scenarios like:
- Read-only API key for reporting tools
- Limited scope keys for third-party integrations
- Service-specific keys with minimal permissions
// Check if current request is authenticated via API key
if (CurrentUser.FindClaim("ApiKeyId") != null)
{
var apiKeyId = CurrentUser.FindClaim("ApiKeyId").Value;
// Additional API key specific logic
}
Performance Considerations
The implementation uses several optimizations:
1. Prefix-based indexing: Database lookups are done by prefix (indexed column), not the full key hash.
2. Distributed caching: API keys are cached after first lookup, dramatically reducing database queries.
// Cache configuration
Configure<AbpDistributedCacheOptions>(options =>
{
options.KeyPrefix = "ApiKey:";
});
3. Cache invalidation: When a key is modified or deleted, cache is automatically invalidated.
Typical Performance:
- Cached lookup: < 5ms
- Database lookup: < 50ms
- Cache hit rate: ~95%
Security Best Practices
When implementing API key authentication, follow these guidelines:
✅ Always use HTTPS - Never send API keys over unencrypted connections
✅ Use different keys per environment - Separate keys for dev, staging, production
❌ Don't log the full key - Only log the prefix for debugging
Getting Started
The complete source code is available on GitHub:
Repository: github.com/salihozkara/AbpApikeyManagement
To integrate it into your ABP project:
- Clone or download the repository
- Add project references to your solution
- Add module dependencies to your modules
- Run EF Core migrations to create the database tables
- Navigate to
/ApiKeyManagementto start managing keys
// In your Web module
[DependsOn(typeof(ApiKeyManagementWebModule))]
public class YourWebModule : AbpModule
{
// ...
}
// In your HttpApi.Host module
[DependsOn(typeof(ApiKeyManagementHttpApiModule))]
public class YourHttpApiHostModule : AbpModule
{
// ...
}
Conclusion
API key authentication remains a crucial part of modern API security, especially for machine-to-machine communication. While it shouldn't replace user authentication methods like OAuth2 for user-facing applications, it's perfect for:
- Automated scripts and tools
- Service-to-service communication
- Third-party integrations
- Long-lived access without token refresh complexity
The implementation shown here demonstrates how ABP Framework's modular architecture, DDD principles, and built-in features (multi-tenancy, caching, permissions) can be leveraged to build a production-ready API key management system.
The solution is open-source and ready to be integrated into your ABP projects. Feel free to explore the code, suggest improvements, or adapt it to your specific needs.
Resources:
- GitHub Repository: salihozkara/AbpApikeyManagement
- ABP Framework: abp.io
- ABP Documentation: docs.abp.io
Happy coding! 🚀
Comments
No one has commented yet, be the first to comment!