Backend: - Cookie + OIDC + API Key authentication schemes - ApiKeyAuthenticationHandler with SHA-256 validation and 24h cache - AuthController with login/logout/profile endpoints - API Key domain model (EventFlow aggregate, events, commands) - ApiKeyReadModel and repository for key validation - Database migration 002_ApiKeys.sql - CORS configuration for frontend Frontend: - authService.ts for login/logout/profile API calls - authStore.ts (Zustand) for user context state - ProtectedRoute component for route guards - Header updated with user display and logout - GraphQL client with credentials: include 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
49 lines
1.5 KiB
C#
49 lines
1.5 KiB
C#
using Books.Api.Domain.ApiKeys.Events;
|
|
using EventFlow.Aggregates;
|
|
|
|
namespace Books.Api.Domain.ApiKeys;
|
|
|
|
public class ApiKeyAggregate : AggregateRoot<ApiKeyAggregate, ApiKeyId>,
|
|
IEmit<ApiKeyCreatedEvent>,
|
|
IEmit<ApiKeyRevokedEvent>
|
|
{
|
|
public new string Name { get; private set; } = string.Empty;
|
|
public string KeyHash { get; private set; } = string.Empty;
|
|
public string CompanyId { get; private set; } = string.Empty;
|
|
public string CreatedBy { get; private set; } = string.Empty;
|
|
public bool IsActive { get; private set; } = true;
|
|
public string? RevokedBy { get; private set; }
|
|
|
|
public ApiKeyAggregate(ApiKeyId id) : base(id) { }
|
|
|
|
public void Create(string name, string keyHash, string companyId, string createdBy)
|
|
{
|
|
if (!IsNew)
|
|
throw new DomainException("APIKEY_EXISTS", "API key already exists", "API nøgle eksisterer allerede");
|
|
|
|
Emit(new ApiKeyCreatedEvent(name, keyHash, companyId, createdBy));
|
|
}
|
|
|
|
public void Revoke(string revokedBy)
|
|
{
|
|
if (!IsActive)
|
|
throw new DomainException("APIKEY_REVOKED", "API key is already revoked", "API nøgle er allerede tilbagekaldt");
|
|
|
|
Emit(new ApiKeyRevokedEvent(revokedBy));
|
|
}
|
|
|
|
public void Apply(ApiKeyCreatedEvent e)
|
|
{
|
|
Name = e.Name;
|
|
KeyHash = e.KeyHash;
|
|
CompanyId = e.CompanyId;
|
|
CreatedBy = e.CreatedBy;
|
|
IsActive = true;
|
|
}
|
|
|
|
public void Apply(ApiKeyRevokedEvent e)
|
|
{
|
|
IsActive = false;
|
|
RevokedBy = e.RevokedBy;
|
|
}
|
|
}
|