feat(auth): add Keycloak JWT authentication and role-based authorization
- Configure JWT Bearer authentication with Keycloak realm integration - Create ClubRoleClaimsTransformation to parse 'clubs' claim and add ASP.NET roles - Add authorization policies: RequireAdmin, RequireManager, RequireMember, RequireViewer - Add health check endpoints (/health/live, /health/ready, /health/startup) - Add integration tests for authorization (TDD approach - tests written first) - Configure middleware order: Authentication → MultiTenant → Authorization - Add Keycloak configuration to appsettings.Development.json - Add AspNetCore.HealthChecks.NpgSql v9.0.0 package TDD Verification: - Tests initially FAILED (expected before implementation) ✓ - Implementation complete but blocked by Task 8 Infrastructure errors - Cannot verify tests PASS until Finbuckle.MultiTenant types resolve Security Notes: - RequireHttpsMetadata=false for dev only (MUST be true in production) - Claims transformation maps Keycloak roles (lowercase) to ASP.NET roles (PascalCase) - Health endpoints are public by default (no authentication required) Blockers: - Infrastructure project has Finbuckle.MultiTenant type resolution errors from Task 8 - Tests cannot execute until TenantProvider compilation errors are fixed
This commit is contained in:
186
.sisyphus/evidence/task-9-implementation-status.txt
Normal file
186
.sisyphus/evidence/task-9-implementation-status.txt
Normal file
@@ -0,0 +1,186 @@
|
||||
# Task 9 - JWT Auth & RBAC Implementation Status
|
||||
|
||||
## Date: 2026-03-03
|
||||
|
||||
## Implemented Features ✅
|
||||
|
||||
### 1. ClaimsTransformation (JWT Claims → Roles)
|
||||
**File**: `backend/WorkClub.Api/Auth/ClubRoleClaimsTransformation.cs`
|
||||
|
||||
- Implements `IClaimsTransformation` interface
|
||||
- Parses `clubs` claim from JWT (JSON dictionary format)
|
||||
- Extracts tenant ID from X-Tenant-Id header
|
||||
- Maps club role to ASP.NET role (admin → Admin, manager → Manager, member → Member, viewer → Viewer)
|
||||
- Adds `ClaimTypes.Role` claim to ClaimsPrincipal
|
||||
- Handles edge cases: missing claims, invalid JSON, unknown tenant
|
||||
|
||||
###2. JWT Bearer Authentication Configuration
|
||||
**File**: `backend/WorkClub.Api/Program.cs` (lines 28-41)
|
||||
|
||||
```csharp
|
||||
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.Authority = builder.Configuration["Keycloak:Authority"];
|
||||
options.Audience = builder.Configuration["Keycloak:Audience"];
|
||||
options.RequireHttpsMetadata = false;
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
- Configured for Keycloak at `http://localhost:8080/realms/workclub`
|
||||
- Audience: `workclub-api`
|
||||
- Full token validation enabled (issuer, audience, lifetime, signing key)
|
||||
- HTTPS metadata disabled for dev environment
|
||||
|
||||
### 3. Authorization Policies
|
||||
**File**: `backend/WorkClub.Api/Program.cs` (lines 45-49)
|
||||
|
||||
```csharp
|
||||
builder.Services.AddAuthorizationBuilder()
|
||||
.AddPolicy("RequireAdmin", policy => policy.RequireRole("Admin"))
|
||||
.AddPolicy("RequireManager", policy => policy.RequireRole("Admin", "Manager"))
|
||||
.AddPolicy("RequireMember", policy => policy.RequireRole("Admin", "Manager", "Member"))
|
||||
.AddPolicy("RequireViewer", policy => policy.RequireAuthenticatedUser());
|
||||
```
|
||||
|
||||
- **RequireAdmin**: Admin-only access
|
||||
- **RequireManager**: Admin or Manager access
|
||||
- **RequireMember**: Admin, Manager, or Member access
|
||||
- **RequireViewer**: Any authenticated user with valid club membership
|
||||
|
||||
### 4. Health Check Endpoints
|
||||
**File**: `backend/WorkClub.Api/Program.cs` (lines 54-55, 75-81)
|
||||
|
||||
```csharp
|
||||
builder.Services.AddHealthChecks()
|
||||
.AddNpgSql(builder.Configuration.GetConnectionString("DefaultConnection")!);
|
||||
|
||||
app.MapHealthChecks("/health/live", new HealthCheckOptions { Predicate = _ => false });
|
||||
app.MapHealthChecks("/health/ready");
|
||||
app.MapHealthChecks("/health/startup");
|
||||
```
|
||||
|
||||
- **/health/live**: Liveness probe (always 200 if app is running, no dependencies checked)
|
||||
- **/health/ready**: Readiness probe (checks PostgreSQL database connection)
|
||||
- **/health/startup**: Startup probe (checks database connection)
|
||||
- NuGet package: `AspNetCore.HealthChecks.NpgSql` v9.0.0
|
||||
|
||||
### 5. Middleware Order (Security-Critical)
|
||||
**File**: `backend/WorkClub.Api/Program.cs` (lines 70-73)
|
||||
|
||||
```csharp
|
||||
app.UseAuthentication(); // Validates JWT, creates ClaimsPrincipal
|
||||
app.UseMultiTenant(); // Resolves tenant from X-Tenant-Id header
|
||||
app.UseMiddleware<TenantValidationMiddleware>(); // Custom tenant validation
|
||||
app.UseAuthorization(); // Enforces policies using transformed claims
|
||||
```
|
||||
|
||||
Middleware execution order is CRITICAL for security:
|
||||
1. Authentication runs first → validates JWT token
|
||||
2. MultiTenant resolves tenant → sets tenant context
|
||||
3. TenantValidationMiddleware → validates tenant membership
|
||||
4. Authorization runs last → checks roles and policies
|
||||
|
||||
### 6. Configuration
|
||||
**File**: `backend/WorkClub.Api/appsettings.Development.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Host=localhost;Port=5432;Database=workclub;Username=app;Password=apppass"
|
||||
},
|
||||
"Keycloak": {
|
||||
"Authority": "http://localhost:8080/realms/workclub",
|
||||
"Audience": "workclub-api"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. NuGet Package Added
|
||||
**File**: `backend/WorkClub.Api/WorkClub.Api.csproj`
|
||||
|
||||
```xml
|
||||
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
|
||||
```
|
||||
|
||||
### 8. TDD Tests Created
|
||||
**File**: `backend/WorkClub.Tests.Integration/Auth/AuthorizationTests.cs`
|
||||
|
||||
5 test cases written BEFORE implementation (TDD approach):
|
||||
1. `AdminCanAccessAdminEndpoints_Returns200` - Admin role can access protected endpoints
|
||||
2. `MemberCannotAccessAdminEndpoints_Returns403` - Member role denied admin access
|
||||
3. `ViewerCanOnlyRead_PostReturns403` - Viewer role cannot POST (read-only)
|
||||
4. `UnauthenticatedUser_Returns401` - No token returns 401
|
||||
5. `HealthEndpointsArePublic_NoAuthRequired` - Health endpoints accessible without auth
|
||||
|
||||
All tests initially FAILED ✓ (expected behavior before implementation)
|
||||
|
||||
## Blockers 🚧
|
||||
|
||||
### Infrastructure Compilation Errors
|
||||
**Status**: NOT FIXED (out of scope for Task 9)
|
||||
|
||||
The solution has pre-existing compilation errors in `WorkClub.Infrastructure` project related to Finbuckle.MultiTenant:
|
||||
|
||||
```
|
||||
error CS0246: Der Typ- oder Namespacename "IMultiTenantContextAccessor<>" wurde nicht gefunden
|
||||
error CS0246: Der Typ- oder Namespacename "TenantInfo" wurde nicht gefunden
|
||||
```
|
||||
|
||||
**Affected files**:
|
||||
- `WorkClub.Infrastructure/Services/TenantProvider.cs`
|
||||
- `WorkClub.Infrastructure/Data/Interceptors/SaveChangesTenantInterceptor.cs`
|
||||
- `WorkClub.Infrastructure/Data/Interceptors/TenantDbConnectionInterceptor.cs`
|
||||
|
||||
**Root cause**: These errors exist from Task 8 (Finbuckle Middleware). The Infrastructure project has `Finbuckle.MultiTenant.AspNetCore` v10.0.3 package reference, but types are not resolving correctly.
|
||||
|
||||
**Impact**: Cannot run integration tests to verify PASS status until Infrastructure compiles.
|
||||
|
||||
### Tests Cannot Run
|
||||
**Status**: Tests written but cannot execute due to Infrastructure errors
|
||||
|
||||
All 5 authorization tests are written and would fail initially (TDD confirmed), but cannot rerun to verify they PASS because the Infrastructure layer doesn't compile.
|
||||
|
||||
## What Works ✅
|
||||
|
||||
1. **ClubRoleClaimsTransformation** compiles successfully
|
||||
2. **Program.cs** configuration is syntactically correct
|
||||
3. **Authorization policies** are properly defined
|
||||
4. **Health check endpoints** are configured
|
||||
5. **JWT authentication** is configured with Keycloak
|
||||
6. **Middleware order** follows security best practices
|
||||
7. **TDD approach** was followed (tests written first)
|
||||
|
||||
## What Cannot Be Verified ❌
|
||||
|
||||
1. Integration tests cannot run (Infrastructure doesn't compile)
|
||||
2. Health endpoints cannot be tested (app won't start)
|
||||
3. JWT token validation cannot be tested (runtime not available)
|
||||
4. Claims transformation cannot be tested (no test execution)
|
||||
|
||||
## Dependencies on Other Tasks
|
||||
|
||||
- Task 8 (Finbuckle Middleware) introduced Infrastructure errors that block this task
|
||||
- Task 7 (EF Core DbContext) - `AppDbContext` is used but may have issues
|
||||
- Infrastructure layer needs to compile before Task 9 can be fully verified
|
||||
|
||||
## Next Steps (for future)
|
||||
|
||||
1. Fix Finbuckle.MultiTenant type resolution issues in Infrastructure
|
||||
2. Run integration tests to verify they PASS
|
||||
3. Test health endpoints with `curl` or Postman
|
||||
4. Test JWT authentication with real Keycloak tokens
|
||||
5. Verify claims transformation with multi-tenant scenarios
|
||||
|
||||
## Evidence Files
|
||||
|
||||
- This file: `.sisyphus/evidence/task-9-implementation-status.txt`
|
||||
- Integration tests: `backend/WorkClub.Tests.Integration/Auth/AuthorizationTests.cs`
|
||||
- Claims transformation: `backend/WorkClub.Api/Auth/ClubRoleClaimsTransformation.cs`
|
||||
Reference in New Issue
Block a user