9.6 KiB
Phase 3: Keycloak Integration - Checklist
✅ Overview
Implement Keycloak JWT authentication, user extraction, and group-based branch access control. Replace placeholder authentication with real Keycloak integration.
📋 Completed Tasks
Keycloak Library
- Create
src/lib/keycloak.ts(300+ lines)- Define KeycloakConfig interface
- Define KeycloakTokenPayload interface
- Implement validateKeycloakToken()
- Implement getUserIdFromRequest()
- Implement getKeycloakGroupsFromRequest()
- Implement getEmailFromRequest()
- Implement getNameFromRequest()
- Implement hasGroup()
- Implement hasAnyGroup()
- Implement getUserInfoFromRequest()
- Implement getKeycloakConfig()
- Implement getMockUserInfo()
- Implement isDevelopmentMode()
Dependencies
- Install jsonwebtoken package
- Install @types/jsonwebtoken
Middleware Integration
- Update
src/middleware/branch.ts- Import Keycloak functions
- Replace extractUserIdFromRequest() with Keycloak version
- Replace extractUserGroupsFromRequest() with Keycloak version
- Add development mode mock support
- Add header-based mock overrides
Documentation
- Create
KEYCLOAK_ENV.md- Document all required environment variables
- Provide Keycloak setup instructions
- Include troubleshooting guide
- Add security best practices
🎯 Key Features Implemented
1. JWT Token Validation
const payload = validateKeycloakToken(token, config);
if (!payload) {
throw new Error("Invalid token");
}
2. User Information Extraction
const userId = getUserIdFromRequest(request);
const groups = getKeycloakGroupsFromRequest(request);
const email = getEmailFromRequest(request);
const name = getNameFromRequest(request);
3. Group-Based Access Control
// Check if user has specific group
if (hasGroup(request, "alla")) {
// User has alla branch access
}
// Check if user has any of multiple groups
if (hasAnyGroup(request, ["alla", "onvalla"])) {
// User has at least one branch access
}
4. Development Mode Support
if (isDevelopmentMode()) {
// Use mock authentication
const userInfo = getMockUserInfo();
}
🔧 Usage Examples
Basic Authentication
import {
getUserIdFromRequest,
getKeycloakGroupsFromRequest,
} from "@/lib/keycloak";
app.get("/api/me", ({ request }) => {
const userId = getUserIdFromRequest(request);
const groups = getKeycloakGroupsFromRequest(request);
return { userId, groups };
});
Check User Permissions
import { hasGroup } from "@/lib/keycloak";
app.post("/admin/action", ({ request }) => {
if (!hasGroup(request, "admin")) {
throw new Error("Forbidden: Admin access required");
}
// Perform admin action
});
Get Complete User Info
import { getUserInfoFromRequest } from "@/lib/keycloak";
app.get("/api/user/profile", ({ request }) => {
const userInfo = getUserInfoFromRequest(request);
return {
userId: userInfo.userId,
email: userInfo.email,
name: userInfo.name,
groups: userInfo.groups,
};
});
Development Mode Testing
# Without Keycloak
curl -H "x-mock-user-id: test-user-123" \
-H "x-mock-groups: alla,onvalla" \
http://localhost:3000/api/customers
# With Keycloak
curl -H "Authorization: Bearer <jwt-token>" \
http://localhost:3000/api/customers
📊 Authentication Flow
Client Request
↓
Branch Middleware
↓
Check NODE_ENV
↓
├─ Development → Use Mock Authentication
│ ├─ Check x-mock-user-id header
│ ├─ Check x-mock-groups header
│ └─ Use default mock if no headers
│
└─ Production → Use Keycloak
├─ Extract Authorization header
├─ Decode JWT token
├─ Verify token expiration
└─ Extract user info (id, groups, email, name)
↓
Validate Branch Access
↓
Inject User Context
↓
Route Handler
🔑 Environment Variables
Required for Production
KEYCLOAK_REALM=alla-os
KEYCLOAK_AUTH_SERVER_URL=https://keycloak.example.com/auth
KEYCLOAK_CLIENT_ID=alla-os-frontend
KEYCLOAK_CLIENT_SECRET=your-secret-here
KEYCLOAK_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
Required for Development
NODE_ENV=development
🚨 Important Notes
Development vs Production
- Development: Uses mock authentication, no JWT required
- Production: Requires valid Keycloak JWT token
- Automatic switching based on
NODE_ENV
Token Structure
Keycloak JWT tokens include:
sub- User ID (UUID)email- User emailname- User full namegroups- User's Keycloak groups (for branch access)realm_access.roles- Realm-level rolesexp- Expiration timestampiat- Issued at timestamp
Group Mapping
Branch access is determined by Keycloak groups:
allagroup → Access to Alla branchonvallagroup → Access to Onvalla branch- Users can have multiple groups for multi-branch access
Token Verification
Currently, tokens are decoded but not verified (signature check is commented out). For production:
- Uncomment JWT verification in
validateKeycloakToken() - Provide valid
KEYCLOAK_PUBLIC_KEY - Test with real Keycloak tokens
🔍 Testing Checklist
Unit Tests (TODO)
- Test validateKeycloakToken() with valid token
- Test validateKeycloakToken() with invalid token
- Test validateKeycloakToken() with expired token
- Test getUserIdFromRequest() with valid JWT
- Test getUserIdFromRequest() without Authorization header
- Test getKeycloakGroupsFromRequest() with groups
- Test getKeycloakGroupsFromRequest() without groups
- Test hasGroup() with existing group
- Test hasGroup() with non-existing group
- Test hasAnyGroup() with multiple groups
- Test getMockUserInfo() default values
- Test getMockUserInfo() with custom values
- Test isDevelopmentMode() in different environments
Integration Tests (TODO)
- Test middleware with development mode
- Test middleware with production mode
- Test mock header overrides
- Test branch access with different groups
- Test unauthorized access
- Test expired token handling
- Test malformed token handling
📝 Known Limitations
-
JWT Signature Not Verified
- Currently only decodes tokens
- Should verify signature in production
- Requires public key configuration
- Action Needed: Uncomment verification code
-
Token Refresh Not Implemented
- No automatic token refresh
- Client must handle token expiration
- Consider implementing refresh token flow
-
Public Key Rotation
- Public key must be manually updated
- Consider fetching from Keycloak endpoint
- Could implement automatic key rotation
-
Error Messages Could Be Generic
- Current errors expose some details
- Could be more generic for security
- Consider logging details separately
🔄 Next Steps
Immediate (Phase 3)
- Write unit tests
- Write integration tests
- Enable JWT signature verification
- Test with real Keycloak instance
- Add token refresh logic
Phase 4: Service Layer Refactor
- Update services to use userId from context
- Implement contact visibility checks
- Add multi-currency calculations
- Implement revision handling
Phase 5: Controllers Update
- Remove authentication placeholders
- Update error handling
- Add user info to responses
- Update API documentation
📊 File Changes
Created Files
- ✅
src/lib/keycloak.ts(300+ lines) - ✅
KEYCLOAK_ENV.md(comprehensive guide)
Modified Files
- ✅
src/middleware/branch.ts(integrated Keycloak functions) - ✅
package.json(added jsonwebtoken dependency)
Documentation Files
- ✅
docs/checklist-phase3-keycloak.md
✨ Success Criteria
- Keycloak library created
- JWT token extraction works
- User ID extraction works
- Group extraction works
- Development mode mocking works
- Middleware integration complete
- Environment variables documented
- Security considerations documented
- Troubleshooting guide provided
- JWT signature verification enabled
- Unit tests pass
- Integration tests pass
- Tested with real Keycloak
🎯 Security Considerations
-
Token Validation ⚠️
- Currently decodes only
- Must verify signature in production
- Check expiration timestamps
-
Environment Variables ✅
- Documented security best practices
- Never commit .env files
- Use strong secrets
-
Error Messages ⚠️
- Consider making more generic
- Don't expose internal details
- Log detailed errors server-side
-
Session Management ✅
- Stateless JWT approach
- No session storage required
- Easy to scale
-
CORS Configuration (TODO)
- Must configure CORS for Keycloak
- Allow proper origins
- Handle preflight requests
📚 References
- Keycloak Documentation
- JWT.io - JWT Debugger
- jsonwebtoken npm
- OpenID Connect
- Keycloak Admin REST API
Phase 3 Status: ✅ CORE COMPLETED (Tests & Signature Verification Pending) Completion Date: 2026-04-23 Next Phase: Phase 4 - Service Layer Refactor Blocking: JWT signature verification, unit tests, integration tests