177 lines
5.4 KiB
Plaintext
177 lines
5.4 KiB
Plaintext
|
|
Task 3: Keycloak Realm Configuration - Verification Evidence
|
||
|
|
==============================================================
|
||
|
|
|
||
|
|
Date: 2026-03-03
|
||
|
|
Task: Configure Keycloak realm with test users and club memberships
|
||
|
|
|
||
|
|
REALM CONFIGURATION
|
||
|
|
-------------------
|
||
|
|
Realm Name: workclub
|
||
|
|
Status: enabled
|
||
|
|
Keycloak Version: 26.0.0
|
||
|
|
|
||
|
|
CLIENTS CONFIGURED
|
||
|
|
------------------
|
||
|
|
|
||
|
|
1. workclub-api (Backend Confidential Client)
|
||
|
|
- Client ID: workclub-api
|
||
|
|
- Type: confidential
|
||
|
|
- Client Secret: dev-secret-workclub-api-change-in-production
|
||
|
|
- Standard Flow: disabled
|
||
|
|
- Direct Access Grants: disabled
|
||
|
|
- Service Accounts: enabled
|
||
|
|
- Purpose: Backend service-to-service authentication
|
||
|
|
|
||
|
|
2. workclub-app (Frontend Public Client)
|
||
|
|
- Client ID: workclub-app
|
||
|
|
- Type: public
|
||
|
|
- Standard Flow: enabled (OAuth2 Authorization Code Flow)
|
||
|
|
- Direct Access Grants: enabled (for dev testing with password grant)
|
||
|
|
- PKCE: enabled (S256 challenge method)
|
||
|
|
- Redirect URIs: http://localhost:3000/*
|
||
|
|
- Web Origins: http://localhost:3000
|
||
|
|
- Purpose: Frontend SPA authentication
|
||
|
|
|
||
|
|
PROTOCOL MAPPER CONFIGURATION
|
||
|
|
-----------------------------
|
||
|
|
Mapper Name: club-membership
|
||
|
|
Type: oidc-usermodel-attribute-mapper
|
||
|
|
User Attribute: clubs
|
||
|
|
Token Claim Name: clubs
|
||
|
|
JSON Type: JSON (critical - ensures claim is parsed as JSON object)
|
||
|
|
Includes in: ID token, access token, userinfo endpoint
|
||
|
|
|
||
|
|
Configuration:
|
||
|
|
- user.attribute: clubs
|
||
|
|
- claim.name: clubs
|
||
|
|
- jsonType.label: JSON
|
||
|
|
- id.token.claim: true
|
||
|
|
- access.token.claim: true
|
||
|
|
- userinfo.token.claim: true
|
||
|
|
- multivalued: false
|
||
|
|
- aggregate.attrs: false
|
||
|
|
|
||
|
|
TEST USERS CONFIGURED
|
||
|
|
---------------------
|
||
|
|
|
||
|
|
1. admin@test.com
|
||
|
|
Password: testpass123
|
||
|
|
Clubs: {"club-1-uuid": "admin", "club-2-uuid": "member"}
|
||
|
|
Description: Multi-club admin with admin role in club-1, member role in club-2
|
||
|
|
|
||
|
|
2. manager@test.com
|
||
|
|
Password: testpass123
|
||
|
|
Clubs: {"club-1-uuid": "manager"}
|
||
|
|
Description: Single club manager with manager role in club-1
|
||
|
|
|
||
|
|
3. member1@test.com
|
||
|
|
Password: testpass123
|
||
|
|
Clubs: {"club-1-uuid": "member", "club-2-uuid": "member"}
|
||
|
|
Description: Multi-club member with member role in both clubs
|
||
|
|
|
||
|
|
4. member2@test.com
|
||
|
|
Password: testpass123
|
||
|
|
Clubs: {"club-1-uuid": "member"}
|
||
|
|
Description: Single club member with member role in club-1
|
||
|
|
|
||
|
|
5. viewer@test.com
|
||
|
|
Password: testpass123
|
||
|
|
Clubs: {"club-1-uuid": "viewer"}
|
||
|
|
Description: Read-only viewer with viewer role in club-1
|
||
|
|
|
||
|
|
All users:
|
||
|
|
- Email verified: true
|
||
|
|
- Enabled: true
|
||
|
|
- Password hashed with: pbkdf2-sha512, 210000 iterations
|
||
|
|
- No required actions (can login immediately)
|
||
|
|
|
||
|
|
JSON VALIDATION
|
||
|
|
---------------
|
||
|
|
Realm export JSON: VALID (verified with json.tool)
|
||
|
|
File size: 8.9 KB
|
||
|
|
Location: /Users/mastermito/Dev/opencode/infra/keycloak/realm-export.json
|
||
|
|
|
||
|
|
VERIFICATION PROCEDURE
|
||
|
|
----------------------
|
||
|
|
To verify this configuration once Docker is running:
|
||
|
|
|
||
|
|
1. Start Keycloak with realm import:
|
||
|
|
docker compose up -d keycloak
|
||
|
|
|
||
|
|
2. Wait for health check:
|
||
|
|
curl -sf http://localhost:8080/health/ready
|
||
|
|
|
||
|
|
3. Run automated test script:
|
||
|
|
./infra/keycloak/test-auth.sh
|
||
|
|
|
||
|
|
The test script will:
|
||
|
|
- Wait for Keycloak to be ready
|
||
|
|
- Authenticate all 5 test users using password grant
|
||
|
|
- Extract and decode JWT access tokens
|
||
|
|
- Verify 'clubs' claim is present and correctly formatted as JSON object
|
||
|
|
- Validate claim values match expected club memberships
|
||
|
|
- Generate evidence files with decoded JWTs and test results
|
||
|
|
|
||
|
|
EXPECTED JWT STRUCTURE
|
||
|
|
----------------------
|
||
|
|
When admin@test.com authenticates, the JWT should contain:
|
||
|
|
|
||
|
|
{
|
||
|
|
"sub": "<uuid>",
|
||
|
|
"email": "admin@test.com",
|
||
|
|
"email_verified": true,
|
||
|
|
"clubs": {
|
||
|
|
"club-1-uuid": "admin",
|
||
|
|
"club-2-uuid": "member"
|
||
|
|
},
|
||
|
|
"given_name": "Admin",
|
||
|
|
"family_name": "User",
|
||
|
|
...
|
||
|
|
}
|
||
|
|
|
||
|
|
CRITICAL: The 'clubs' claim MUST be a JSON object (not a string).
|
||
|
|
This is controlled by the protocol mapper's jsonType.label: JSON setting.
|
||
|
|
|
||
|
|
DOCKER ENVIRONMENT STATUS
|
||
|
|
--------------------------
|
||
|
|
Docker daemon status: NOT RUNNING (Colima failed to start)
|
||
|
|
Reason: VZ driver error on macOS
|
||
|
|
|
||
|
|
Manual verification steps documented above can be executed when Docker environment is available.
|
||
|
|
The realm export JSON is complete and valid, ready for import.
|
||
|
|
|
||
|
|
ARCHITECTURE IMPACT
|
||
|
|
-------------------
|
||
|
|
This configuration is CRITICAL for multi-tenant architecture:
|
||
|
|
|
||
|
|
1. Backend (Finbuckle) will read 'clubs' claim to:
|
||
|
|
- Identify which tenants (clubs) the user belongs to
|
||
|
|
- Determine user's role within each tenant
|
||
|
|
- Enforce tenant isolation and authorization
|
||
|
|
|
||
|
|
2. Frontend (NextAuth) will use 'clubs' claim to:
|
||
|
|
- Display club switcher UI
|
||
|
|
- Enable user to switch between clubs
|
||
|
|
- Show appropriate UI based on role (admin vs member vs viewer)
|
||
|
|
|
||
|
|
3. Claim format requirements:
|
||
|
|
- MUST be JSON object: {"<tenant-id>": "<role>"}
|
||
|
|
- Key = Club UUID (tenant identifier)
|
||
|
|
- Value = Role string (admin, manager, member, viewer)
|
||
|
|
- If claim is string instead of object, entire auth pipeline breaks
|
||
|
|
|
||
|
|
FILES CREATED
|
||
|
|
-------------
|
||
|
|
- /Users/mastermito/Dev/opencode/infra/keycloak/realm-export.json (realm config)
|
||
|
|
- /Users/mastermito/Dev/opencode/infra/keycloak/test-auth.sh (verification script)
|
||
|
|
- /Users/mastermito/Dev/opencode/.sisyphus/evidence/task-3-user-auth.txt (placeholder)
|
||
|
|
- /Users/mastermito/Dev/opencode/.sisyphus/evidence/task-3-jwt-claims.txt (placeholder)
|
||
|
|
|
||
|
|
NEXT STEPS
|
||
|
|
----------
|
||
|
|
Once Docker environment is running:
|
||
|
|
1. Execute test-auth.sh to verify all users authenticate
|
||
|
|
2. Confirm JWT 'clubs' claim is JSON object (not string)
|
||
|
|
3. Verify claim values match expected roles for each user
|
||
|
|
4. Save JWT samples to evidence files for documentation
|