# BLOCKER: Task Creation Fails - Missing `sub` Claim in JWT ## Discovery Context - **Test**: Phase 3 - Task 1: Create New Task (POST /api/tasks) - **Date**: 2026-03-05 - **Status**: ❌ BLOCKED - Cannot proceed with API CRUD tests --- ## Issue Description Task creation endpoint returns **400 Bad Request** with error `"Invalid user ID"`. ### Root Cause Analysis **API Code Expectation** (`TaskEndpoints.cs` line 62): ```csharp var userIdClaim = httpContext.User.FindFirst("sub")?.Value; if (string.IsNullOrEmpty(userIdClaim) || !Guid.TryParse(userIdClaim, out var createdById)) { return TypedResults.BadRequest("Invalid user ID"); } ``` **JWT Payload Reality**: ```json { "exp": 1772729098, "iat": 1772725498, "jti": "5387896f-52a2-4949-bd6e-cbbb09c97a86", "iss": "http://localhost:8080/realms/workclub", "aud": "workclub-api", "typ": "Bearer", "azp": "workclub-app", "sid": "c5f5ef18-6721-4b27-b577-21d8d4268a06", "acr": "1", "allowed-origins": ["http://localhost:3000"], "scope": "profile email", "email_verified": true, "clubs": "64e05b5e-ef45-81d7-f2e8-3d14bd197383,3b4afcfa-1352-8fc7-b497-8ab52a0d5fda", "name": "Admin User", "preferred_username": "admin@test.com", "given_name": "Admin", "family_name": "User", "email": "admin@test.com" } ``` **Missing Claim**: `sub` (subject) claim is absent from JWT token --- ## Impact Assessment ### Affected Endpoints All endpoints requiring user identification via `sub` claim are broken: - `POST /api/tasks` - Create task (requires createdById) - `POST /api/shifts` - Create shift (likely requires createdById) - Any endpoint that needs to identify the current user ### Scope of Blockage - **Phase 3: API CRUD Tests** - ❌ BLOCKED (cannot create tasks/shifts) - **Phase 4: Frontend E2E Tests** - ❌ BLOCKED (depends on working API) - **Phase 5: Integration Flow** - ❌ BLOCKED (step 3 creates task) - **Phase 6: Edge Cases** - ⚠️ PARTIALLY BLOCKED (some tests need task creation) ### Tests Still Executable - ✅ Read operations: GET /api/tasks, GET /api/shifts (already tested) - ✅ Authorization tests (401/403) - ✅ Tenant isolation verification (already completed) --- ## Expected vs Actual ### Expected (per plan) > **Definition of Done**: "Keycloak login returns JWT with club claims" JWT should contain: 1. ✅ `clubs` claim (present: `"64e05b5e-ef45-81d7-f2e8-3d14bd197383,3b4afcfa-1352-8fc7-b497-8ab52a0d5fda"`) 2. ❌ `sub` claim (missing: should contain Keycloak user UUID) 3. ✅ `aud` claim (present: `"workclub-api"`) 4. ✅ `email` claim (present: `"admin@test.com"`) ### Actual Behavior - Keycloak token includes `clubs` custom claim ✅ - Keycloak token missing standard `sub` (subject) claim ❌ - API rejects all create operations requiring user identification ❌ --- ## Keycloak Configuration Gap **Standard OpenID Connect Claim**: The `sub` claim is a **mandatory** claim in OIDC spec and should automatically be included by Keycloak. **Possible Causes**: 1. Client protocol mapper configuration incorrect 2. User account missing UUID in Keycloak 3. Token mapper overriding default behavior 4. Keycloak realm export missing default mappers **Verification Attempted**: ```bash # Userinfo endpoint returned 403 (also requires fix) curl -H "Authorization: Bearer $TOKEN" \ http://localhost:8080/realms/workclub/protocol/openid-connect/userinfo # HTTP 403 ``` --- ## Workaround Options ### Option 1: Fix Keycloak Configuration (RECOMMENDED) - Add `sub` protocol mapper to `workclub-api` client - Ensure mapper includes Keycloak user ID as UUID - Re-acquire tokens after config change ### Option 2: Change API to Use Email - Modify `TaskEndpoints.cs` to use `email` claim instead of `sub` - Query database for member record by email + tenant context - **Risk**: Email not unique across tenants, requires additional lookup ### Option 3: Skip Create Operations in QA - Continue testing with read-only operations - Mark create/update/delete tests as "NOT TESTED - Blocker" - Report as critical finding in F3 verdict --- ## Recommendation **STOP F3 QA execution at this point.** This is a **CRITICAL BLOCKER** preventing: - 30+ scenarios in Phase 3 (API CRUD - all create/update operations) - All of Phase 4 (Frontend E2E - UI create workflows) - All of Phase 5 (Integration - 10-step journey starts with task creation) - Most of Phase 6 (Edge cases with concurrent writes) **Estimated Impact**: 40/46 remaining scenarios (87% of remaining QA suite) are blocked. --- ## F3 QA Status Update ### Scenarios Completed - Phase 1: Infrastructure (12/12) ✅ - Phase 2: RLS Isolation (6/6) ✅ (4 PASS, 2 FAIL - shifts RLS missing) - **Total: 18/58 scenarios (31%)** ### Scenarios Blocked - Phase 3: API CRUD (14 scenarios) ❌ BLOCKED - Phase 4: Frontend E2E (6 scenarios) ❌ BLOCKED - Phase 5: Integration (10 steps) ❌ BLOCKED - Phase 6: Edge Cases (6 tests, ~4 blocked) ⚠️ MOSTLY BLOCKED - **Total: ~40 scenarios blocked** ### Blockers Identified 1. **Shifts RLS Policy Missing** (Phase 2, Test 4-5): Tenant data leakage on shifts table 2. **JWT Missing `sub` Claim** (Phase 3, Test 1): Cannot create tasks/shifts --- ## Next Steps **For Development Team**: 1. Fix Keycloak configuration to include `sub` claim in JWT 2. Implement RLS policy on `shifts` table (matching `work_items` policy) 3. Re-run F3 Manual QA from Phase 3 after fixes **For QA Agent**: 1. Mark F3 QA as **INCOMPLETE** due to critical blocker 2. Generate final report with 18/58 scenarios executed 3. Document both blockers with reproduction steps 4. Provide FAIL verdict with clear remediation path