Keycloak OIDC Setup Tutorial¶
This tutorial walks you through setting up Keycloak Single Sign-On (SSO) authentication for ContextForge, enabling enterprise identity management with the popular open-source identity and access management solution.
Quick Start (Docker Compose)¶
Use the preconfigured local SSO stack to test Keycloak end-to-end without external IdP setup:
Or run directly with compose:
Preconfigured local endpoints and credentials:
- Gateway UI:
http://localhost:8080/admin/login - Keycloak admin console:
http://localhost:8180(admin/changeme) - Keycloak realm:
mcp-gateway - Keycloak client:
mcp-gateway - Callback URL:
http://localhost:8080/auth/sso/callback/keycloak - Compose wiring: internal Keycloak URL
http://keycloak:8080, browser URLhttp://localhost:8180 - Test users (all password
changeme): admin@example.comdeveloper@example.comviewer@example.comnewuser@example.com
Prerequisites¶
- ContextForge installed and running
- Keycloak instance with admin access (self-hosted or cloud)
- Keycloak admin console access with appropriate permissions
- Access to your gateway's environment configuration
Step 1: Access Keycloak Admin Console¶
1.1 Log into Keycloak¶
- Navigate to your Keycloak admin console (typically at
https://keycloak.yourcompany.com/admin) - Log in with your administrator credentials
-
Select the realm you want to use (or create a new one)
-
Default realm:
master - For production, consider creating a dedicated realm
1.2 Create or Select Realm¶
Creating a New Realm (Optional):
- Hover over the realm dropdown in the top-left corner
- Click Create Realm
-
Enter realm details:
-
Realm name:
mcp-gateway(or your preferred name) -
Enabled: Yes
-
Click Create
Using Existing Realm:
- If using the default
masterrealm or an existing realm, simply select it from the dropdown
Step 2: Create Client in Keycloak¶
2.1 Navigate to Clients¶
- In the left sidebar, click Clients
- Click Create client button
- You'll configure the client in the following steps
2.2 Configure Client Settings - General Settings¶
On the General Settings page:
Client type: OpenID Connect
Client ID: mcp-gateway - This is your SSO_KEYCLOAK_CLIENT_ID - Use a descriptive name for your organization
Click Next
2.3 Configure Client Settings - Capability Config¶
On the Capability config page:
Client authentication: On (required for confidential clients)
Authorization: Off (not needed for basic SSO)
Authentication flow: Select the following:
- ✅ Standard flow - Enables authorization code flow (required)
- ✅ Direct access grants - Enables direct access (optional, for API access)
- ❌ Implicit flow - Leave unchecked (deprecated)
- ❌ Service accounts roles - Leave unchecked (unless needed)
Click Next
2.4 Configure Client Settings - Login Settings¶
On the Login settings page:
Root URL: https://gateway.yourcompany.com - For development: http://localhost:8000
Home URL: https://gateway.yourcompany.com
Valid redirect URIs: https://gateway.yourcompany.com/auth/sso/callback/keycloak - For development, add: http://localhost:8000/auth/sso/callback/keycloak - You can add multiple redirect URIs (one per line)
Valid post logout redirect URIs: https://gateway.yourcompany.com/admin/login - Redirects users after logout
Web origins: https://gateway.yourcompany.com - For CORS support - Use + to allow all valid redirect URIs
Click Save
2.5 Note Client Credentials¶
After creating the client:
- Navigate to the Credentials tab
- Copy the Client secret value - This is your
SSO_KEYCLOAK_CLIENT_SECRET - IMPORTANT: Store this secret securely (use a password manager or vault)
- The Client ID is visible in the Settings tab
Step 3: Configure Client Scopes and Mappers¶
3.1 Configure Client Scopes¶
Keycloak includes default scopes for OpenID Connect. Verify these are enabled:
- In your client's settings, go to Client scopes tab
-
Verify these Assigned default client scopes:
-
✅
email- Email address scope - ✅
profile- Basic profile information - ✅
roles- User roles - ✅
web-origins- CORS origins
If any are missing, click Add client scope and select them from the Default type.
3.2 Add Custom Mappers (Optional)¶
To include additional claims in tokens:
- Go to Client scopes → Select a scope (e.g.,
profile) - Go to Mappers tab
- Click Add mapper → By configuration
- Choose mapper type based on what you need:
Group Membership Mapper:
- Name:
groups - Mapper Type:
Group Membership - Token Claim Name:
groups - Full group path: Off (for simple group names)
- Add to ID token: On
- Add to access token: On
- Add to userinfo: On
User Attribute Mapper (for custom attributes):
- Name:
department - Mapper Type:
User Attribute - User Attribute:
department - Token Claim Name:
department - Claim JSON Type:
String - Add to ID token: On
- Add to access token: On
- Add to userinfo: On
Step 4: Configure Realm and Client Roles¶
4.1 Create Realm Roles¶
Realm roles apply across all clients in the realm:
- In the left sidebar, click Realm roles
- Click Create role
- Create roles for your organization:
Example roles:
- Role name:
gateway-admin - Description: Administrator role for ContextForge
- Click Save
Repeat for additional roles:
gateway-user- Standard usergateway-developer- Developer accessgateway-viewer- Read-only access
4.2 Create Client Roles (Optional)¶
Client roles are specific to ContextForge client:
- Navigate to Clients → Select your
mcp-gatewayclient - Go to Roles tab
- Click Create role
- Create client-specific roles:
Example roles:
admin- Client adminmember- Client memberviewer- Client viewer
4.3 Assign Roles to Users¶
- Go to Users in the left sidebar
- Find and select a user
- Go to Role mapping tab
- Click Assign role
-
Select roles to assign:
-
Filter by Realm roles or client name
- Check desired roles
- Click Assign
4.4 Configure Role Mappers¶
To include roles in JWT tokens:
- Go to Client scopes → roles
- Go to Mappers tab
- Verify these mappers exist:
realm roles:
- Maps realm roles to
realm_access.rolesclaim - Exists by default
client roles:
- Maps client roles to
resource_access.{client_id}.rolesclaim - Exists by default
If missing, create them manually using Add mapper → By configuration → User Realm Role or User Client Role.
Important — enable on ID token and userinfo, not just the access token:
Keycloak's built-in realm roles and client roles mappers ship with only Add to access token turned on. Add to ID token and Add to userinfo are off by default. ContextForge reads roles from the userinfo response (and falls back to the ID token in some configurations) — it does not parse the access token — so with the defaults left as-is, realm_access/resource_access never reach the gateway and SSO_KEYCLOAK_ROLE_MAPPINGS/SSO_KEYCLOAK_MAP_REALM_ROLES silently have nothing to map, even though the role is assigned correctly in Keycloak.
For each of the realm roles and client roles mappers, open it and switch on:
- Add to ID token
- Add to userinfo
(in addition to the default Add to access token). The local infra/keycloak/realm-export.json dev seed already configures these mappers correctly — this step is only needed when wiring up your own Keycloak realm by hand.
Step 5: Configure User Attributes and Groups¶
5.1 Create Groups (Optional)¶
Groups provide hierarchical organization:
- In the left sidebar, click Groups
- Click Create group
-
Enter group details:
-
Name:
Developers - Click Create
Create additional groups as needed:
AdministratorsOperationsSupport
5.2 Assign Users to Groups¶
- Go to Users → Select a user
- Go to Groups tab
- Click Join Group
- Select desired groups
- Click Join
5.3 Add Group Mappers¶
To include group membership in tokens:
- Go to Client scopes → profile (or create custom scope)
- Go to Mappers tab
- Verify or create Group Membership mapper (see Step 3.2)
Step 6: Configure ContextForge Environment¶
6.1 Keycloak Auto-Discovery Feature¶
Keycloak provides OpenID Connect auto-discovery, which reduces configuration from 10 environment variables to just 6:
- No need to specify: Authorization URL, Token URL, Userinfo URL, JWKS URI
- Only specify: Base URL, Realm, Client ID, Client Secret
The gateway automatically discovers endpoints from:
6.2 Update Environment Variables¶
Add these variables to your .env file:
# Enable SSO System
SSO_ENABLED=true
# Keycloak OIDC Configuration
SSO_KEYCLOAK_ENABLED=true
SSO_KEYCLOAK_BASE_URL=https://keycloak.yourcompany.com
SSO_KEYCLOAK_REALM=master
SSO_KEYCLOAK_CLIENT_ID=mcp-gateway
SSO_KEYCLOAK_CLIENT_SECRET=your-client-secret-from-keycloak
# Optional: Role mapping configuration
SSO_KEYCLOAK_MAP_REALM_ROLES=true
SSO_KEYCLOAK_MAP_CLIENT_ROLES=false
# Optional: Custom JWT claim mapping
SSO_KEYCLOAK_USERNAME_CLAIM=preferred_username
SSO_KEYCLOAK_EMAIL_CLAIM=email
SSO_KEYCLOAK_GROUPS_CLAIM=groups
# Optional: Auto-create users on first login
SSO_AUTO_CREATE_USERS=true
# Optional: Restrict to corporate email domains
SSO_TRUSTED_DOMAINS=["yourcompany.com"]
# Optional: Preserve local admin authentication
SSO_PRESERVE_ADMIN_AUTH=true
6.3 Example Production Configuration¶
# Production Keycloak SSO Setup
SSO_ENABLED=true
SSO_KEYCLOAK_ENABLED=true
SSO_KEYCLOAK_BASE_URL=https://keycloak.acmecorp.com
SSO_KEYCLOAK_REALM=production
SSO_KEYCLOAK_CLIENT_ID=mcp-gateway-prod
SSO_KEYCLOAK_CLIENT_SECRET=AbC~dEf1GhI2jKl3MnO4pQr5StU6vWx7YzA8bcD9efG0
# Role mapping - include realm roles, exclude client roles
SSO_KEYCLOAK_MAP_REALM_ROLES=true
SSO_KEYCLOAK_MAP_CLIENT_ROLES=false
# Custom claims for enterprise directory
SSO_KEYCLOAK_USERNAME_CLAIM=preferred_username
SSO_KEYCLOAK_EMAIL_CLAIM=email
SSO_KEYCLOAK_GROUPS_CLAIM=groups
# Enterprise security settings
SSO_AUTO_CREATE_USERS=true
SSO_TRUSTED_DOMAINS=["acmecorp.com"]
SSO_PRESERVE_ADMIN_AUTH=true
6.4 Development Configuration¶
# Development Keycloak SSO Setup
SSO_ENABLED=true
SSO_KEYCLOAK_ENABLED=true
SSO_KEYCLOAK_BASE_URL=http://localhost:8080
SSO_KEYCLOAK_REALM=master
SSO_KEYCLOAK_CLIENT_ID=mcp-gateway-dev
SSO_KEYCLOAK_CLIENT_SECRET=dev-client-secret-value
# More permissive for testing
SSO_KEYCLOAK_MAP_REALM_ROLES=true
SSO_KEYCLOAK_MAP_CLIENT_ROLES=true
SSO_AUTO_CREATE_USERS=true
SSO_PRESERVE_ADMIN_AUTH=true
6.5 Multi-Realm Configuration¶
For organizations with multiple realms:
# Development Realm
SSO_KEYCLOAK_REALM=development
SSO_KEYCLOAK_CLIENT_ID=mcp-gateway-dev
# Redirect: https://gateway-dev.yourcompany.com/auth/sso/callback/keycloak
# Staging Realm
SSO_KEYCLOAK_REALM=staging
SSO_KEYCLOAK_CLIENT_ID=mcp-gateway-staging
# Redirect: https://gateway-staging.yourcompany.com/auth/sso/callback/keycloak
# Production Realm
SSO_KEYCLOAK_REALM=production
SSO_KEYCLOAK_CLIENT_ID=mcp-gateway-prod
# Redirect: https://gateway.yourcompany.com/auth/sso/callback/keycloak
6.6 Configuration Variables Reference¶
| Variable | Required | Default | Description |
|---|---|---|---|
SSO_KEYCLOAK_ENABLED | Yes | false | Enable Keycloak SSO provider |
SSO_KEYCLOAK_BASE_URL | Yes | - | Base URL of Keycloak instance |
SSO_KEYCLOAK_PUBLIC_BASE_URL | No | unset | Browser-facing Keycloak URL used for authorization redirects and issuer verification when gateway uses an internal URL |
SSO_KEYCLOAK_REALM | Yes | master | Keycloak realm name |
SSO_KEYCLOAK_CLIENT_ID | Yes | - | OAuth client ID from Keycloak |
SSO_KEYCLOAK_CLIENT_SECRET | Yes | - | OAuth client secret from Keycloak |
SSO_KEYCLOAK_MAP_REALM_ROLES | No | true | Include realm roles in user profile |
SSO_KEYCLOAK_MAP_CLIENT_ROLES | No | false | Include client-specific roles |
SSO_KEYCLOAK_USERNAME_CLAIM | No | preferred_username | JWT claim for username |
SSO_KEYCLOAK_EMAIL_CLAIM | No | email | JWT claim for email |
SSO_KEYCLOAK_GROUPS_CLAIM | No | groups | JWT claim for group membership |
SSO_KEYCLOAK_ROLE_MAPPINGS | No | {} | JSON map of Keycloak roles/groups to Gateway RBAC roles |
SSO_KEYCLOAK_DEFAULT_ROLE | No | unset | Fallback role when no mapping matches |
SSO_KEYCLOAK_RESOLVE_TEAM_SCOPE_TO_PERSONAL_TEAM | No | false | Resolve team-scoped mapped roles to the user's personal team |
Step 7: Restart and Verify Gateway¶
7.1 Restart the Gateway¶
# Development
make dev
# Or directly with uvicorn
uvicorn mcpgateway.main:app --reload --host 0.0.0.0 --port 8000
# Production
make serve
7.2 Verify Keycloak SSO is Enabled¶
Test that Keycloak appears in SSO providers:
# Check if Keycloak is listed
curl -X GET http://localhost:8000/auth/sso/providers
# Should return Keycloak in the list:
[
{
"id": "keycloak",
"name": "keycloak",
"display_name": "Keycloak"
}
]
7.3 Check Startup Logs¶
Verify no errors in the logs:
# Look for SSO initialization messages
tail -f logs/gateway.log | grep -i keycloak
# Should see:
# INFO: SSO provider 'keycloak' initialized successfully
# INFO: Keycloak auto-discovery loaded endpoints from https://.../.well-known/openid-configuration
7.4 Test Auto-Discovery Endpoint¶
Verify Keycloak's OIDC discovery endpoint is accessible:
# Test OIDC discovery
curl https://keycloak.yourcompany.com/realms/master/.well-known/openid-configuration
# Should return JSON with endpoints:
{
"issuer": "https://keycloak.yourcompany.com/realms/master",
"authorization_endpoint": "https://keycloak.yourcompany.com/realms/master/protocol/openid-connect/auth",
"token_endpoint": "https://keycloak.yourcompany.com/realms/master/protocol/openid-connect/token",
"userinfo_endpoint": "https://keycloak.yourcompany.com/realms/master/protocol/openid-connect/userinfo",
...
}
Step 8: Test Keycloak SSO Login¶
8.1 Access Login Page¶
-
Navigate to your gateway's login page:
-
Development:
http://localhost:8000/admin/login -
Production:
https://gateway.yourcompany.com/admin/login -
You should see a "Keycloak" button with a key icon
8.2 Test Authentication Flow¶
- Click Continue with Keycloak
- You'll be redirected to Keycloak's sign-in page
- Enter your Keycloak username and password
- Complete multi-factor authentication if configured in Keycloak
- Grant consent for the application if prompted (first-time users)
- You'll be redirected back to the gateway admin panel
- You should be logged in successfully
8.3 Verify User Creation¶
Check that a user was created in the gateway:
# Using the admin API (requires admin token)
curl -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
http://localhost:8000/auth/users
# Look for your Keycloak email in the user list
8.4 Verify User Profile and Roles¶
Check that user attributes and roles were imported correctly:
# Get user details
curl -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
http://localhost:8000/auth/users/{user_id}
# Verify fields are populated:
# - email: your@company.com
# - full_name: First Last
# - provider: keycloak
# - provider_id: unique-keycloak-id
# - roles: [list of realm/client roles if mapped]
# - groups: [list of groups if mapped]
8.5 Test Role Mapping¶
If SSO_KEYCLOAK_MAP_REALM_ROLES=true, verify roles are included:
# Decode JWT token to inspect claims
# Use https://jwt.io or a JWT decoding library
# Expected claims:
{
"realm_access": {
"roles": ["gateway-admin", "gateway-user"]
},
"resource_access": {
"mcp-gateway": {
"roles": ["admin", "member"] # If MAP_CLIENT_ROLES=true
}
},
"groups": ["/Developers", "/Administrators"]
}
Step 9: Configure Advanced Features¶
9.1 Multi-Factor Authentication (MFA)¶
Configure MFA in Keycloak for enhanced security:
- Go to Authentication in the left sidebar
- Select Flows tab
-
Configure Browser flow:
-
Add OTP Form execution
-
Set to Required
-
Go to Required Actions tab
- Enable Configure OTP for first-time setup
Users will be prompted to configure OTP (Google Authenticator, etc.) on first login.
9.2 Conditional Authentication¶
Create custom authentication flows:
- Go to Authentication → Flows
- Click Create flow
-
Add conditional logic:
-
Condition - User Role: Check if user has specific role
- Condition - User Attribute: Check custom attributes
- OTP Form: Require MFA for admins only
9.3 Identity Brokering¶
Connect Keycloak to external identity providers:
- Go to Identity Providers in the left sidebar
- Click Add provider
-
Select provider type:
-
GitHub - Social login
- Google - Google Workspace
- Microsoft - Azure AD/Entra ID
- SAML v2.0 - Enterprise SAML providers
- OpenID Connect - Generic OIDC providers
Configure the provider and users can login through Keycloak using external accounts.
9.4 User Federation¶
Sync users from LDAP/Active Directory:
- Go to User federation in the left sidebar
- Click Add LDAP providers (or Kerberos)
-
Configure connection:
-
Connection URL:
ldap://ldap.company.com:389 - Bind DN: Service account DN
-
Bind Credential: Service account password
-
Configure user mapping:
-
User Object Classes:
person, organizationalPerson, user - Username LDAP attribute:
sAMAccountName - RDN LDAP attribute:
cn -
UUID LDAP attribute:
objectGUID -
Click Test connection and Test authentication
- Click Save
- Click Synchronize all users to import LDAP users
9.5 Custom Themes and Branding¶
Customize Keycloak's login page appearance:
- Go to Realm settings → Themes tab
-
Configure themes:
-
Login theme: Custom branded login page
- Account theme: Custom account management UI
-
Email theme: Branded email templates
-
Or create custom theme:
-
Place theme files in
themes/{theme-name}/directory - Update realm theme settings
- Restart Keycloak
9.6 Events and Monitoring¶
Configure audit logging:
- Go to Realm settings → Events tab
-
Login events settings:
-
Enable Save Events: Yes
- Set Expiration: 7 days (or longer for compliance)
-
Select events to log (Login, Logout, Register, etc.)
-
Admin events settings:
-
Enable Save Events: Yes
- Enable Include Representation: Yes (for detailed audit)
View events:
- Events → Login events: User authentication events
- Events → Admin events: Configuration changes
Step 10: Production Deployment Checklist¶
10.1 Security Requirements¶
- HTTPS enforced for all redirect URIs
- Client secrets stored securely (HashiCorp Vault, Kubernetes Secrets)
- MFA enabled for administrators
- MFA recommended or required for all users
- Strong password policies configured
- Session timeout configured appropriately
- Brute force detection enabled
10.2 Keycloak Configuration¶
- Client created with correct settings
- Client ID and client secret documented securely
- Redirect URIs match production URLs exactly
- Client scopes configured (email, profile, roles, groups)
- Required mappers added for custom claims
- Realm roles defined and assigned
- Client roles defined (if needed)
- Users assigned to appropriate roles
- Groups configured (if using group-based access)
10.3 Gateway Configuration¶
- Environment variables configured correctly
- Trusted domains configured for email restrictions
-
SSO_AUTO_CREATE_USERSset appropriately -
SSO_PRESERVE_ADMIN_AUTHenabled (recommended) - Role mapping settings configured (
MAP_REALM_ROLES,MAP_CLIENT_ROLES) - Custom claim mapping configured (username, email, groups)
- Logs configured for audit trail
- Auto-discovery endpoint accessible from gateway
10.4 Keycloak Hardening¶
- Keycloak database secured (TLS, strong passwords)
- Keycloak admin console access restricted (IP allowlist)
- Keycloak running behind reverse proxy (HTTPS termination)
- Keycloak realm settings reviewed for security
- Password policies enforced (complexity, history, expiration)
- Brute force detection enabled (failed login lockout)
- Events logging enabled for audit compliance
- LDAP/AD integration secured (if applicable)
- Regular Keycloak updates applied
10.5 Monitoring and Compliance¶
- Keycloak login events monitoring enabled
- Keycloak admin events monitoring enabled
- Gateway SSO logs reviewed regularly
- Alerting configured for authentication failures
- Regular access reviews scheduled
- Compliance reporting configured (if required)
Troubleshooting¶
Error: "SSO authentication is disabled"¶
Problem: SSO endpoints return 404 Solution: Set SSO_ENABLED=true and SSO_KEYCLOAK_ENABLED=true, then restart gateway
Error: "Invalid client credentials"¶
Problem: Wrong client ID or client secret Solution: Verify credentials from Keycloak admin console match exactly
# Double-check these values from Keycloak client settings
SSO_KEYCLOAK_CLIENT_ID=mcp-gateway # From client General Settings
SSO_KEYCLOAK_CLIENT_SECRET=your-actual-secret # From client Credentials tab
SSO_KEYCLOAK_REALM=master # Realm name (case-sensitive)
Verify in Keycloak:
- Go to Clients → Select your client
- Settings tab: Verify Client ID
- Credentials tab: Regenerate secret if needed
Error: "redirect_uri_mismatch"¶
Problem: Keycloak redirect URI doesn't match Solution: Verify exact URL match in Keycloak client configuration
# Keycloak redirect URI must exactly match:
https://your-domain.com/auth/sso/callback/keycloak
# Common mistakes:
https://your-domain.com/auth/sso/callback/keycloak/ # Extra slash
http://your-domain.com/auth/sso/callback/keycloak # HTTP instead of HTTPS
https://your-domain.com/auth/sso/callback/generic # Wrong provider ID
To fix:
- Go to Keycloak Admin Console → Clients → Your client
- Go to Settings tab
- Update Valid redirect URIs field
- Add exact callback URL (one per line)
- Click Save
Error: "User not found" or "Email not verified"¶
Problem: User email not verified in Keycloak Solution: Verify user's email in Keycloak
- Go to Users → Find user
- Go to Details tab
- Check Email verified: Set to On
- Click Save
Or configure Keycloak to skip email verification:
- Go to Realm settings → Login tab
- Disable Verify email
Error: "Failed to discover OIDC endpoints"¶
Problem: Auto-discovery endpoint not accessible Solution: Verify Keycloak base URL and realm are correct
# Test discovery endpoint manually
curl https://keycloak.yourcompany.com/realms/master/.well-known/openid-configuration
# Should return JSON with OIDC endpoints
# If this fails, check:
# - Base URL is correct and accessible from gateway
# - Realm name is spelled correctly (case-sensitive)
# - Keycloak is running and healthy
# - Network/firewall rules allow access
Error: "Invalid issuer in JWT token"¶
Problem: JWT issuer doesn't match expected value Solution: Verify realm and base URL configuration
# Expected issuer format:
https://keycloak.yourcompany.com/realms/{realm}
# Common issues:
# - Base URL includes "/auth" path for older Keycloak versions (pre-17)
# - Realm name mismatch (case-sensitive)
# - Base URL with trailing slash
# For Keycloak < 17.0, use:
SSO_KEYCLOAK_BASE_URL=https://keycloak.yourcompany.com/auth
# For Keycloak >= 17.0, use:
SSO_KEYCLOAK_BASE_URL=https://keycloak.yourcompany.com
Split-URL deployments (e.g., Docker Compose with reverse proxy): When the gateway reaches Keycloak via an internal URL (SSO_KEYCLOAK_BASE_URL=http://keycloak:8080) but users authenticate via a different public URL, set SSO_KEYCLOAK_PUBLIC_BASE_URL to the browser-facing URL. The gateway rewrites both the authorization URL and the issuer to the public base so that id_token issuer verification succeeds:
SSO_KEYCLOAK_BASE_URL=http://keycloak:8080 # Internal (token, userinfo, JWKS)
SSO_KEYCLOAK_PUBLIC_BASE_URL=http://localhost:8180 # Browser-facing (auth URL, issuer)
Roles Not Appearing in JWT¶
Problem: User roles not included in JWT token Solution: Configure role mappers and enable role mapping
-
Verify role mappers exist and reach the ID token / userinfo:
-
Go to Client scopes → roles → Mappers
- Ensure realm roles and client roles mappers exist
-
Open each mapper and confirm Add to ID token and Add to userinfo are both ON — Keycloak ships these mappers with only Add to access token enabled by default, and ContextForge reads roles from userinfo/ID token, not the access token. This is the most common cause of "role assigned in Keycloak but gateway treats user as default role" — see Step 4.4.
-
Enable role mapping in gateway:
-
Assign roles to user:
-
Go to Users → Select user → Role mapping
- Assign appropriate roles
ID Token Signature Verification Failed¶
Problem: Gateway rejects ID tokens with signature verification errors Solution: Ensure JWKS endpoint is accessible and properly configured
The gateway cryptographically verifies ID token signatures using Keycloak's JWKS endpoint. Common issues:
1. JWKS Endpoint Unreachable
# Test JWKS endpoint accessibility from gateway
curl https://keycloak.yourcompany.com/realms/master/protocol/openid-connect/certs
# Should return JSON with public keys
# If this fails, check network/firewall rules
2. Issuer Mismatch (Split-URL Deployments)
When Keycloak is accessed via different URLs (internal vs. public), the ID token issuer may not match:
# Gateway uses internal URL, but ID token has public issuer
SSO_KEYCLOAK_BASE_URL=http://keycloak:8080 # Internal
SSO_KEYCLOAK_PUBLIC_BASE_URL=http://localhost:8180 # Public (browser-facing)
The gateway automatically rewrites the issuer to match the public URL for verification.
3. Clock Skew
ID tokens have expiration times. Ensure gateway and Keycloak servers have synchronized clocks:
# Check time on both servers
date -u # Should be within 5 minutes of each other
# Sync with NTP if needed
sudo ntpdate -s time.nist.gov
4. Manual JWKS URI Configuration
If auto-discovery fails, manually specify the JWKS endpoint:
# For generic OIDC provider configuration
SSO_GENERIC_JWKS_URI=https://keycloak.yourcompany.com/realms/master/protocol/openid-connect/certs
5. Certificate/TLS Issues
If Keycloak uses self-signed certificates:
# Development only - skip TLS verification
SKIP_SSL_VERIFY=true
# Production - add CA certificate to system trust store
# Or configure custom CA bundle
Groups Not Appearing in JWT¶
Problem: User groups not included in JWT token Solution: Add group membership mapper
- Go to Client scopes → profile (or custom scope)
- Go to Mappers tab
- Click Add mapper → By configuration
-
Select Group Membership:
-
Name:
groups - Token Claim Name:
groups - Full group path: Off
- Add to ID token: On
-
Add to userinfo: On
-
Click Save
Update gateway configuration:
Keycloak Admin Console Not Accessible¶
Problem: Cannot access Keycloak admin console Solution: Check Keycloak configuration and network
-
Verify Keycloak is running:
-
Check network and firewall rules
- Verify admin user credentials
- Reset admin password if needed:
Testing Checklist¶
- Client created in Keycloak admin console
- Client ID and secret copied securely
- Redirect URIs configured correctly
- Client scopes assigned (email, profile, roles)
- Mappers configured for custom claims
- Realm roles created and assigned
- Users assigned to appropriate roles
- Groups configured (if applicable)
- Environment variables configured in gateway
- Gateway restarted with new config
-
/auth/sso/providersreturns Keycloak provider - Login page shows Keycloak button
- Authentication flow completes successfully
- User created in gateway user list
- User profile populated with correct data
- Roles included in JWT (if
MAP_REALM_ROLES=true) - Groups included in JWT (if groups mapper configured)
- MFA working (if configured)
- LDAP sync working (if configured)
Security Best Practices¶
Secret Management¶
DO:
- ✅ Store client secrets in secure vault (HashiCorp Vault, Kubernetes Secrets)
- ✅ Rotate secrets regularly (every 90-180 days)
- ✅ Use separate clients for dev/staging/prod
- ✅ Use separate realms for environment isolation
DON'T:
- ❌ Store secrets in source control
- ❌ Share secrets via email or chat
- ❌ Use the same secret across environments
- ❌ Reuse secrets after exposure
Access Control¶
- Principle of Least Privilege: Only grant necessary roles
- Role-Based Access: Use realm roles for broad access, client roles for specific permissions
- Group-Based Management: Manage users via groups for easier administration
- Regular Reviews: Audit user access quarterly
- Time-Limited Sessions: Configure appropriate session timeouts
Keycloak Hardening¶
- Admin Console Access: Restrict to specific IPs or VPN
- Database Security: Use TLS for database connections, strong passwords
- HTTPS Only: Never run Keycloak on HTTP in production
- Brute Force Protection: Enable lockout after failed login attempts
- Password Policies: Enforce strong passwords (length, complexity, history)
- Regular Updates: Keep Keycloak updated with latest security patches
Monitoring¶
- Enable Login events in Keycloak for authentication audit trail
- Enable Admin events for configuration change tracking
- Configure Alerts for suspicious authentication patterns
- Review Event logs regularly for security incidents
- Export logs to SIEM for centralized monitoring
Benefits of Keycloak SSO¶
Simplified Configuration¶
Keycloak's auto-discovery reduces configuration by 40% compared to generic OIDC:
Generic OIDC requires:
- Authorization URL
- Token URL
- Userinfo URL
- Issuer URL
- JWKS URI (sometimes)
Keycloak requires only:
- Base URL
- Realm name
- Client ID
- Client secret
The gateway automatically discovers all endpoints from the well-known configuration.
Enterprise Features¶
- User Federation: LDAP/Active Directory synchronization
- Identity Brokering: Connect to external IdPs (Google, GitHub, SAML)
- Multi-Factor Authentication: Built-in OTP and WebAuthn support
- Fine-Grained Authorization: Realm and client roles, group-based access
- Customizable UI: Branded login pages and themes
- Events and Audit: Comprehensive logging for compliance
Open Source Advantages¶
- Self-Hosted: Full control over identity data
- No Vendor Lock-In: Standards-based OIDC/OAuth 2.0
- Active Community: Large ecosystem and plugin support
- Cost-Effective: No per-user licensing fees
- Extensible: Custom authenticators, mappers, and providers
Next Steps¶
After Keycloak SSO is working:
- Configure MFA for enhanced security
- Set up user federation with LDAP/AD (if applicable)
- Configure identity brokering for social login options
- Implement fine-grained authorization with custom roles
- Customize login themes for brand consistency
- Set up event logging for audit compliance
- Configure session management and timeout policies
- Implement group-based team mapping in ContextForge
- Document your configuration for team reference
Machine-to-Machine (Service Account) API Access¶
In addition to interactive browser login, ContextForge can accept Keycloak access tokens directly as Bearer credentials on API/MCP endpoints — letting automation clients authenticate with client_credentials tokens, without a browser. See SSO: Machine-to-machine API auth with external IdP tokens for the overall design and security caveats.
1. Create a Confidential Client with Service Accounts Enabled¶
- In the Keycloak admin console, go to Clients > Create client.
- Set Client ID to something like
mcp-agentand Client authentication toOn(confidential client). - Under Capability config, enable Service accounts roles (this allows the
client_credentialsgrant). - Save the client and note its Client secret under the Credentials tab.
2. Add an Audience Mapper¶
Keycloak does not include the client itself in aud by default. Add a mapper so tokens issued to mcp-agent carry the audience ContextForge expects:
- Go to Clients >
mcp-agent> Client scopes >mcp-agent-dedicated> Mappers > Add mapper > By configuration. - Choose Audience.
- Set Included Client Audience (or Included Custom Audience) to the value you will configure as
api_audiencefor this provider, e.g.mcp-gateway. - Set Add to access token to
On. Save.
3. Assign Roles/Groups to the Service Account¶
- Go to Clients >
mcp-agent> Service accounts roles. - Assign realm or client roles (or add the service account user to a group under Users >
service-account-mcp-agent> Groups). - These roles/groups map to ContextForge teams/roles via the same
SSO_KEYCLOAK_ROLE_MAPPINGSconfiguration used for browser SSO (see Step 6.6 reference).
4. Configure ContextForge¶
Then opt this provider in via the provider API, setting api_audience to match the audience mapper from step 2:
curl -X PUT https://gateway.yourcompany.com/auth/sso/admin/providers/keycloak \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"trusted_for_api_auth": true,
"api_audience": "mcp-gateway"
}'
5. Fetch a Token and Call the API¶
# Fetch a client_credentials access token from Keycloak
TOKEN=$(curl -s -X POST \
https://keycloak.yourcompany.com/realms/master/protocol/openid-connect/token \
-d "grant_type=client_credentials" \
-d "client_id=mcp-agent" \
-d "client_secret=$MCP_AGENT_CLIENT_SECRET" \
| jq -r '.access_token')
# Call ContextForge using the Keycloak access token directly
curl -H "Authorization: Bearer $TOKEN" \
https://gateway.yourcompany.com/tools
6. Service Principal Identity¶
Since service account tokens have no email claim, ContextForge provisions a synthetic local user svc-mcp-agent@keycloak.service.local for this client, with teams/roles derived from the role/group mappings configured in step 3.
Revocation
ContextForge cannot revoke this token before its Keycloak-issued expiry. Configure a short access token lifespan for the mcp-agent client (Keycloak: Clients > mcp-agent > Advanced > Access Token Lifespan) if the service account may need to be revoked quickly.
Related Documentation¶
- Complete SSO Guide - Full SSO documentation
- GitHub SSO Tutorial - GitHub setup guide
- Google SSO Tutorial - Google setup guide
- IBM Security Verify Tutorial - IBM setup guide
- Microsoft Entra ID Tutorial - Microsoft Entra ID setup guide
- Okta SSO Tutorial - Okta setup guide
- Generic OIDC Tutorial - Generic OIDC providers
- Team Management - Managing teams and roles
- RBAC Configuration - Role-based access control
Support and Resources¶
Keycloak Documentation¶
- Keycloak Official Documentation
- Server Administration Guide
- Securing Applications Guide
- Authorization Services Guide
Troubleshooting Resources¶
- Keycloak Server Logs: Check Keycloak logs for detailed authentication errors
- Gateway logs: Enable
LOG_LEVEL=DEBUGfor detailed SSO flow logging - OIDC Discovery: Test
.well-known/openid-configurationendpoint - Keycloak Community: Active community forums and mailing lists
Getting Help¶
If you encounter issues:
- Check Keycloak server logs for error messages
- Verify OIDC discovery endpoint is accessible
- Enable debug logging in gateway:
LOG_LEVEL=DEBUG - Review gateway logs for Keycloak-specific errors
- Test JWT token claims at jwt.io
- Consult Keycloak documentation and community forums
- Check ContextForge issue tracker