Files
temporal/IMPLEMENTATION_CHECKLIST.md
Christian Galo 02b4ec9ee3 Add JWT-based authorization support for Temporal server with Keycloak integration
- Create QUICK_REFERENCE.md for a concise guide on setting up temporal authorization.
- Add README_AUTHORIZATION.md detailing the implementation steps and common issues.
- Introduce REVERSE_PROXY_APPROACH.md as an alternative method for authorization using a reverse proxy.
- Implement Dockerfile for building a custom Temporal server with authorization features.
- Add main.go to initialize the custom Temporal server with JWT authorization.
- Create example-keycloak-mapper.json for mapping Keycloak groups to Temporal permissions.
- Add development.yaml for configuring the Temporal server with JWT settings.
- Implement test-authorization.sh script to verify JWT token claims and Temporal server access.
- Include go.mod for managing Go dependencies in the custom server.
- Document troubleshooting steps and customization options in README.md.
2025-10-24 02:10:54 +00:00

11 KiB

Temporal Authorization Implementation Checklist

Use this checklist to implement authorization step-by-step.

Phase 1: Understanding (15 mins)

  • Read README_AUTHORIZATION.md (high-level overview)
  • Review DIAGRAMS.md (understand the flow)
  • Read QUICK_REFERENCE.md (understand permission format)
  • Understand the concept: Groups → Permissions → Allow/Deny

Key Concept: Users in Keycloak groups get permissions like "namespace:role" in their JWT, which Temporal uses to decide what they can do.


Phase 2: Verify Current Setup (15 mins)

  • Verify Temporal UI authentication is working
  • Test that you can login via Keycloak
  • Confirm TEMPORAL_AUTH_ISSUER_URL environment variable is set
  • Run test script: ./test-authorization.sh
  • Note: JWT probably does NOT have permissions claim yet (that's OK)

Expected Result: You can login, but JWT doesn't have permissions yet.


Phase 3: Configure Keycloak (30 mins)

3.1: Create Groups

Login to Keycloak Admin Console:

  • Navigate to: Groups
  • Create group: temporal-admins
  • Create group: dev-team
  • Create group: ops-team
  • Create group: viewers

3.2: Add Protocol Mapper

  • Navigate to: Clients → [your-temporal-ui-client]
  • Go to: Client Scopes tab
  • Select the client scope (usually named same as client)
  • Click: Mappers tab
  • Click: Add mapper → By configuration
  • Select: Script Mapper
  • Configure:
    • Name: temporal-permissions
    • Mapper Type: Script Mapper
    • Token Claim Name: permissions
    • Claim JSON Type: JSON
    • Add to ID token: ON
    • Add to access token: ON
    • Add to userinfo: ON
    • Multivalued: ON
    • Script: Copy from custom-server/example-keycloak-mapper.json
  • Click: Save

3.3: Customize Mapper (Optional)

Edit the script in the mapper to match your organization:

// Example: Map your groups to permissions
if (groupName === 'your-admin-group') {
    permissions.add('temporal-system:admin');
}
else if (groupName === 'your-dev-group') {
    permissions.add('development:write');
    permissions.add('staging:write');
}
// ... etc
  • Customize group-to-permission mappings if needed
  • Save changes

3.4: Assign Users to Groups

  • Navigate to: Users
  • Select a test user
  • Go to: Groups tab
  • Click: Join Group
  • Add user to dev-team (or another group)
  • Repeat for other test users

3.5: Verify JWT

  • Run test script again: ./test-authorization.sh
  • Verify JWT now contains permissions array
  • Verify permissions match expected format: "namespace:role"

Expected Result: JWT contains permissions like ["development:write", "staging:write"]


Phase 4: Prepare Custom Server (15 mins)

4.1: Review Configuration

  • Review custom-server/config/development.yaml
  • Verify JWKS URL format: https://your-keycloak.com/realms/your-realm/protocol/openid-connect/certs
  • Update if your realm name is different

4.2: Review Server Code

  • Review custom-server/main.go
  • Understand: It uses Temporal's default ClaimMapper and Authorizer
  • No code changes needed for basic setup

4.3: Create go.sum

cd /srv/temporal/custom-server
go mod download
go mod tidy
  • Run commands above
  • Verify go.sum is created
  • Check for any errors

Phase 5: Update Docker Compose (10 mins)

5.1: Backup Current Config

cp compose.yaml compose.yaml.backup
  • Create backup

5.2: Update Temporal Service

Edit compose.yaml:

temporal:
  # OLD: image: temporalio/auto-setup:1.29.0
  # NEW:
  build:
    context: ./custom-server
    dockerfile: Dockerfile
  # ... rest stays the same
  • Change from image: to build:
  • Point to ./custom-server directory

5.3: Add Environment Variable

Make sure this exists in temporal service:

environment:
  # ... existing vars
  - TEMPORAL_AUTH_ISSUER_URL=${TEMPORAL_AUTH_ISSUER_URL}
  • Verify TEMPORAL_AUTH_ISSUER_URL is in environment
  • Verify it's defined in your .env file

Phase 6: Build and Deploy (15 mins)

6.1: Build Custom Server

cd /srv/temporal
docker-compose build temporal
  • Run build command
  • Wait for build to complete (may take 5-10 mins first time)
  • Check for errors

6.2: Deploy

If using Docker Compose:

docker-compose down
docker-compose up -d

If using Docker Swarm:

docker stack deploy temporal --detach=true -c compose.yaml
  • Deploy updated stack
  • Wait for services to start

6.3: Verify Deployment

# Check logs
docker-compose logs temporal
# OR
docker service logs temporal_temporal

# Look for:
# "Starting Temporal Server with JWT Authorization..."
# "All services are started"
  • Check logs for startup
  • Verify no errors
  • Confirm server is running

Phase 7: Create Namespaces (5 mins)

Create namespaces that match your permission names:

# As admin user (in temporal-admins group)
docker exec -it <temporal-container> tctl namespace register development
docker exec -it <temporal-container> tctl namespace register staging
docker exec -it <temporal-container> tctl namespace register production
  • Create development namespace
  • Create staging namespace
  • Create production namespace
  • Create any other namespaces you need

Phase 8: Test Authorization (30 mins)

8.1: Test Admin User

  • Login as user in temporal-admins group
  • Verify can access all namespaces
  • Verify can start workflows
  • Verify can terminate workflows
  • Verify can see all operations

8.2: Test Developer User

  • Login as user in dev-team group
  • Verify can access development namespace
  • Verify can start workflow in development
  • Verify can access production namespace (read-only)
  • Try to start workflow in production - should FAIL ✗
  • Verify error message: "PermissionDenied"

8.3: Test Viewer User

  • Login as user in viewers group
  • Verify can view workflows in all namespaces
  • Try to start any workflow - should FAIL ✗
  • Try to terminate workflow - should FAIL ✗
  • Verify can only perform read operations

8.4: Test No-Access User

  • Login as user NOT in any Temporal groups
  • Try to access any namespace
  • Should see: PermissionDenied or no data

8.5: Verify Logs

# Check authorization decisions in logs
docker-compose logs temporal | grep -i "authoriz"
  • Check logs show authorization checks
  • Look for Allow/Deny decisions
  • Verify permissions are being extracted from JWT

Phase 9: Production Hardening (Optional)

9.1: Security

  • Use HTTPS for Keycloak (if not already)
  • Use HTTPS for Temporal UI (if not already)
  • Verify JWT tokens have short expiration (15-60 mins)
  • Set up key rotation in Keycloak
  • Review Temporal audit logging

9.2: Monitoring

  • Add authorization metrics to monitoring
  • Set up alerts for frequent PermissionDenied errors
  • Monitor JWT validation failures

9.3: Documentation

  • Document your permission scheme for team
  • Create onboarding guide for new users
  • Document which groups map to which permissions
  • Create troubleshooting guide

Phase 10: Maintenance

10.1: Adding New Users

  • Add user to Keycloak
  • Assign to appropriate group
  • User can immediately login with correct permissions

10.2: Changing Permissions

  • Move user to different group in Keycloak
  • User gets new permissions on next login
  • (JWT refresh may take up to token expiration time)

10.3: Adding New Namespaces

  • Create namespace in Temporal
  • Update Keycloak mapper script to include new namespace
  • Users in appropriate groups get access

Troubleshooting

If things go wrong, check:

Issue: "PermissionDenied" for all users

Check:

  • JWT contains permissions claim (run ./test-authorization.sh)
  • Permissions format is correct: "namespace:role"
  • JWKS URL is accessible from Temporal server
  • Temporal server logs show JWT validation success

Fix:

  • Review Keycloak mapper configuration
  • Verify permissionsClaimName in config matches JWT claim
  • Check network connectivity to Keycloak

Issue: Server won't start

Check:

  • Docker build completed successfully
  • go.mod and go.sum exist
  • Configuration YAML is valid
  • Environment variables are set

Fix:

  • Check server logs: docker logs <container>
  • Verify YAML syntax: yamllint custom-server/config/development.yaml
  • Rebuild: docker-compose build --no-cache temporal

Issue: JWT signature validation fails

Check:

  • JWKS endpoint is correct
  • JWKS endpoint is accessible from container
  • Token hasn't expired
  • Issuer matches expected value

Fix:

  • Test JWKS: curl https://keycloak.com/realms/realm/protocol/openid-connect/certs
  • Verify TEMPORAL_AUTH_ISSUER_URL environment variable
  • Check Temporal can reach Keycloak (network/firewall)

Issue: UI can't connect to server

Check:

  • Temporal server is running
  • No errors in server logs
  • UI can reach server (network)
  • JWT is being passed from UI to server

Fix:

  • Restart UI: docker-compose restart ui
  • Check UI logs for auth errors
  • Verify UI environment variables are correct

📊 Success Criteria

You'll know it's working when:

  1. Different users see different things in Temporal UI
  2. Users in dev-team can manage dev namespace but not production
  3. Users in viewers can only view, not modify
  4. Users in temporal-admins can do everything
  5. Users not in any group are denied access
  6. Server logs show authorization checks happening
  7. "PermissionDenied" errors appear for unauthorized actions

📚 Reference

  • Full Guide: AUTHORIZATION_GUIDE.md
  • Quick Reference: QUICK_REFERENCE.md
  • Flow Diagrams: DIAGRAMS.md
  • Test Script: ./test-authorization.sh
  • Alternative Approach: REVERSE_PROXY_APPROACH.md

⏱️ Time Tracking

  • Phase 1: Understanding (15 mins)
  • Phase 2: Verify Setup (15 mins)
  • Phase 3: Configure Keycloak (30 mins)
  • Phase 4: Prepare Server (15 mins)
  • Phase 5: Update Compose (10 mins)
  • Phase 6: Build & Deploy (15 mins)
  • Phase 7: Create Namespaces (5 mins)
  • Phase 8: Test (30 mins)

Total: ~2.5 hours


🎯 Current Status

Track your progress:

  • Started implementation
  • Keycloak configured
  • Server built
  • Server deployed
  • Basic testing done
  • All test cases passing
  • Production ready
  • Documentation complete

Last updated: _______________


Good luck with your implementation! 🚀