feat(backend): Implement MCP API Key Management System (Story 5.2)
Implemented comprehensive API Key authentication and management system
for MCP Server to ensure only authorized AI agents can access ColaFlow.
## Domain Layer
- Created McpApiKey aggregate root with BCrypt password hashing
- Implemented ApiKeyPermissions value object (read/write, resource/tool filtering)
- Added ApiKeyStatus enum (Active, Revoked)
- Created domain events (ApiKeyCreatedEvent, ApiKeyRevokedEvent)
- API key format: cola_<36 random chars> (cryptographically secure)
- Default expiration: 90 days
## Application Layer
- Implemented McpApiKeyService with full CRUD operations
- Created DTOs for API key creation, validation, and updates
- Validation logic: hash verification, expiration check, IP whitelist
- Usage tracking: last_used_at, usage_count
## Infrastructure Layer
- Created McpDbContext with PostgreSQL configuration
- EF Core entity configuration with JSONB for permissions/IP whitelist
- Implemented McpApiKeyRepository with prefix-based lookup
- Database migration: mcp_api_keys table with indexes
- Created McpApiKeyAuthenticationMiddleware for API key validation
- Middleware validates Authorization: Bearer <api_key> header
## API Layer
- Created McpApiKeysController with REST endpoints:
- POST /api/mcp/keys - Create API Key (returns plain key once!)
- GET /api/mcp/keys - List tenant's API Keys
- GET /api/mcp/keys/{id} - Get API Key details
- PATCH /api/mcp/keys/{id}/metadata - Update name/description
- PATCH /api/mcp/keys/{id}/permissions - Update permissions
- DELETE /api/mcp/keys/{id} - Revoke API Key
- Requires JWT authentication (not API key auth)
## Testing
- Created 17 unit tests for McpApiKey entity
- Created 7 unit tests for ApiKeyPermissions value object
- All 49 tests passing (including existing MCP tests)
- Test coverage > 80% for Domain layer
## Security Features
- BCrypt hashing with work factor 12
- API key shown only once at creation (never logged)
- Key prefix lookup for fast validation (indexed)
- Multi-tenant isolation (tenant_id filter)
- IP whitelist support
- Permission scopes (read/write, resources, tools)
- Automatic expiration after 90 days
## Database Schema
Table: mcp.mcp_api_keys
- Indexes: key_prefix (unique), tenant_id, tenant_user, expires_at, status
- JSONB columns for permissions and IP whitelist
- Soft delete via revoked_at
## Integration
- Updated Program.cs to register MCP module with configuration
- Added MCP DbContext migration in development mode
- Authentication middleware runs before MCP protocol handler
Changes:
- Created 31 new files (2321+ lines)
- Domain: 6 files (McpApiKey, events, repository, value objects)
- Application: 9 files (service, DTOs)
- Infrastructure: 8 files (DbContext, repository, middleware, migration)
- API: 1 file (McpApiKeysController)
- Tests: 2 files (17 + 7 unit tests)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// DTO for API Key permissions
|
||||
/// </summary>
|
||||
public class ApiKeyPermissionsDto
|
||||
{
|
||||
public bool Read { get; set; }
|
||||
public bool Write { get; set; }
|
||||
public List<string> AllowedResources { get; set; } = new();
|
||||
public List<string> AllowedTools { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// Response DTO for API Key (without plain key)
|
||||
/// </summary>
|
||||
public class ApiKeyResponse
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid TenantId { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public required string KeyPrefix { get; set; }
|
||||
public required string Status { get; set; }
|
||||
public required ApiKeyPermissionsDto Permissions { get; set; }
|
||||
public List<string>? IpWhitelist { get; set; }
|
||||
public DateTime? LastUsedAt { get; set; }
|
||||
public long UsageCount { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public DateTime? RevokedAt { get; set; }
|
||||
public Guid? RevokedBy { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using ColaFlow.Modules.Mcp.Domain.ValueObjects;
|
||||
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// Result of API Key validation
|
||||
/// </summary>
|
||||
public class ApiKeyValidationResult
|
||||
{
|
||||
public bool IsValid { get; private set; }
|
||||
public string? ErrorMessage { get; private set; }
|
||||
public Guid ApiKeyId { get; private set; }
|
||||
public Guid TenantId { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
public ApiKeyPermissions? Permissions { get; private set; }
|
||||
|
||||
private ApiKeyValidationResult()
|
||||
{
|
||||
}
|
||||
|
||||
public static ApiKeyValidationResult Valid(
|
||||
Guid apiKeyId,
|
||||
Guid tenantId,
|
||||
Guid userId,
|
||||
ApiKeyPermissions permissions)
|
||||
{
|
||||
return new ApiKeyValidationResult
|
||||
{
|
||||
IsValid = true,
|
||||
ApiKeyId = apiKeyId,
|
||||
TenantId = tenantId,
|
||||
UserId = userId,
|
||||
Permissions = permissions
|
||||
};
|
||||
}
|
||||
|
||||
public static ApiKeyValidationResult Invalid(string errorMessage)
|
||||
{
|
||||
return new ApiKeyValidationResult
|
||||
{
|
||||
IsValid = false,
|
||||
ErrorMessage = errorMessage
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// Request DTO for creating a new API Key
|
||||
/// </summary>
|
||||
public class CreateApiKeyRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Friendly name for the API key
|
||||
/// </summary>
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional description
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tenant ID
|
||||
/// </summary>
|
||||
public required Guid TenantId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User ID who creates the key
|
||||
/// </summary>
|
||||
public required Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow read access
|
||||
/// </summary>
|
||||
public bool Read { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Allow write access
|
||||
/// </summary>
|
||||
public bool Write { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// List of allowed resource URIs (empty = all allowed)
|
||||
/// </summary>
|
||||
public List<string>? AllowedResources { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of allowed tool names (empty = all allowed)
|
||||
/// </summary>
|
||||
public List<string>? AllowedTools { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional IP whitelist
|
||||
/// </summary>
|
||||
public List<string>? IpWhitelist { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of days until expiration (default: 90)
|
||||
/// </summary>
|
||||
public int ExpirationDays { get; set; } = 90;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// Response DTO for created API Key
|
||||
/// IMPORTANT: PlainKey is only shown once at creation!
|
||||
/// </summary>
|
||||
public class CreateApiKeyResponse
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// IMPORTANT: Plain API Key - shown only once at creation!
|
||||
/// Save this securely - it cannot be retrieved later.
|
||||
/// </summary>
|
||||
public required string PlainKey { get; set; }
|
||||
|
||||
public required string KeyPrefix { get; set; }
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public required ApiKeyPermissionsDto Permissions { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// Request DTO for updating API Key metadata
|
||||
/// </summary>
|
||||
public class UpdateApiKeyMetadataRequest
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// Request DTO for updating API Key permissions
|
||||
/// </summary>
|
||||
public class UpdateApiKeyPermissionsRequest
|
||||
{
|
||||
public bool Read { get; set; }
|
||||
public bool Write { get; set; }
|
||||
public List<string>? AllowedResources { get; set; }
|
||||
public List<string>? AllowedTools { get; set; }
|
||||
public List<string>? IpWhitelist { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user