setup
This commit is contained in:
381
docs/checklist-phase3-keycloak.md
Normal file
381
docs/checklist-phase3-keycloak.md
Normal file
@@ -0,0 +1,381 @@
|
||||
# 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
|
||||
|
||||
- [x] Create `src/lib/keycloak.ts` (300+ lines)
|
||||
- [x] Define KeycloakConfig interface
|
||||
- [x] Define KeycloakTokenPayload interface
|
||||
- [x] Implement validateKeycloakToken()
|
||||
- [x] Implement getUserIdFromRequest()
|
||||
- [x] Implement getKeycloakGroupsFromRequest()
|
||||
- [x] Implement getEmailFromRequest()
|
||||
- [x] Implement getNameFromRequest()
|
||||
- [x] Implement hasGroup()
|
||||
- [x] Implement hasAnyGroup()
|
||||
- [x] Implement getUserInfoFromRequest()
|
||||
- [x] Implement getKeycloakConfig()
|
||||
- [x] Implement getMockUserInfo()
|
||||
- [x] Implement isDevelopmentMode()
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [x] Install jsonwebtoken package
|
||||
- [x] Install @types/jsonwebtoken
|
||||
|
||||
### Middleware Integration
|
||||
|
||||
- [x] Update `src/middleware/branch.ts`
|
||||
- [x] Import Keycloak functions
|
||||
- [x] Replace extractUserIdFromRequest() with Keycloak version
|
||||
- [x] Replace extractUserGroupsFromRequest() with Keycloak version
|
||||
- [x] Add development mode mock support
|
||||
- [x] Add header-based mock overrides
|
||||
|
||||
### Documentation
|
||||
|
||||
- [x] Create `KEYCLOAK_ENV.md`
|
||||
- [x] Document all required environment variables
|
||||
- [x] Provide Keycloak setup instructions
|
||||
- [x] Include troubleshooting guide
|
||||
- [x] Add security best practices
|
||||
|
||||
## 🎯 Key Features Implemented
|
||||
|
||||
### 1. JWT Token Validation
|
||||
|
||||
```typescript
|
||||
const payload = validateKeycloakToken(token, config);
|
||||
if (!payload) {
|
||||
throw new Error("Invalid token");
|
||||
}
|
||||
```
|
||||
|
||||
### 2. User Information Extraction
|
||||
|
||||
```typescript
|
||||
const userId = getUserIdFromRequest(request);
|
||||
const groups = getKeycloakGroupsFromRequest(request);
|
||||
const email = getEmailFromRequest(request);
|
||||
const name = getNameFromRequest(request);
|
||||
```
|
||||
|
||||
### 3. Group-Based Access Control
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
if (isDevelopmentMode()) {
|
||||
// Use mock authentication
|
||||
const userInfo = getMockUserInfo();
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 Usage Examples
|
||||
|
||||
### Basic Authentication
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```env
|
||||
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
|
||||
|
||||
```env
|
||||
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 email
|
||||
- `name` - User full name
|
||||
- `groups` - User's Keycloak groups (for branch access)
|
||||
- `realm_access.roles` - Realm-level roles
|
||||
- `exp` - Expiration timestamp
|
||||
- `iat` - Issued at timestamp
|
||||
|
||||
### Group Mapping
|
||||
|
||||
Branch access is determined by Keycloak groups:
|
||||
|
||||
- `alla` group → Access to Alla branch
|
||||
- `onvalla` group → 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:
|
||||
|
||||
1. Uncomment JWT verification in `validateKeycloakToken()`
|
||||
2. Provide valid `KEYCLOAK_PUBLIC_KEY`
|
||||
3. 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
|
||||
|
||||
1. **JWT Signature Not Verified**
|
||||
- Currently only decodes tokens
|
||||
- Should verify signature in production
|
||||
- Requires public key configuration
|
||||
- **Action Needed**: Uncomment verification code
|
||||
|
||||
2. **Token Refresh Not Implemented**
|
||||
- No automatic token refresh
|
||||
- Client must handle token expiration
|
||||
- Consider implementing refresh token flow
|
||||
|
||||
3. **Public Key Rotation**
|
||||
- Public key must be manually updated
|
||||
- Consider fetching from Keycloak endpoint
|
||||
- Could implement automatic key rotation
|
||||
|
||||
4. **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
|
||||
|
||||
- [x] Keycloak library created
|
||||
- [x] JWT token extraction works
|
||||
- [x] User ID extraction works
|
||||
- [x] Group extraction works
|
||||
- [x] Development mode mocking works
|
||||
- [x] Middleware integration complete
|
||||
- [x] Environment variables documented
|
||||
- [x] Security considerations documented
|
||||
- [x] Troubleshooting guide provided
|
||||
- [ ] JWT signature verification enabled
|
||||
- [ ] Unit tests pass
|
||||
- [ ] Integration tests pass
|
||||
- [ ] Tested with real Keycloak
|
||||
|
||||
## 🎯 Security Considerations
|
||||
|
||||
1. **Token Validation** ⚠️
|
||||
- Currently decodes only
|
||||
- Must verify signature in production
|
||||
- Check expiration timestamps
|
||||
|
||||
2. **Environment Variables** ✅
|
||||
- Documented security best practices
|
||||
- Never commit .env files
|
||||
- Use strong secrets
|
||||
|
||||
3. **Error Messages** ⚠️
|
||||
- Consider making more generic
|
||||
- Don't expose internal details
|
||||
- Log detailed errors server-side
|
||||
|
||||
4. **Session Management** ✅
|
||||
- Stateless JWT approach
|
||||
- No session storage required
|
||||
- Easy to scale
|
||||
|
||||
5. **CORS Configuration** (TODO)
|
||||
- Must configure CORS for Keycloak
|
||||
- Allow proper origins
|
||||
- Handle preflight requests
|
||||
|
||||
## 📚 References
|
||||
|
||||
- [Keycloak Documentation](https://www.keycloak.org/documentation)
|
||||
- [JWT.io](https://jwt.io/) - JWT Debugger
|
||||
- [jsonwebtoken npm](https://www.npmjs.com/package/jsonwebtoken)
|
||||
- [OpenID Connect](https://openid.net/connect/)
|
||||
- [Keycloak Admin REST API](https://www.keycloak.org/docs-api/latest/rest-api/index.html)
|
||||
|
||||
---
|
||||
|
||||
**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
|
||||
Reference in New Issue
Block a user