Files
nextjs-elysia-allaos/docs/checklist-phase3-keycloak.md
phaichayon 043edff93a setup
2026-04-26 00:15:22 +07:00

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 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

  • 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

  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


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