Keycloak RBAC
This guide maps Keycloak client roles onto Endatix roles. The API keeps the access token small and loads roles from the Keycloak token introspection endpoint. Introspection runs only when Authorization.RoleMappings has at least one entry.
For a general overview of external authorization concepts and benefits, see the External Authorization guide.
Prerequisites
- Keycloak authentication already configured (see Keycloak Setup)
- Admin access to Keycloak realm
- Endatix API and Endatix Hub running
Dev realm
The Endatix API realm file already defines client roles on endatix-hub and assigns them. Password for each user is admin@2.
| User | Client role | Endatix role |
|---|---|---|
external-admin@endatix.com | admin | Admin |
external-creator@endatix.com | creator | Creator |
respondent@endatix.com | respondent | Respondent |
external@endatix.com | none | none beyond Authenticated |
external-admin@endatix.com is on endatix.com. The dev realm turns on Always use lightweight access token for endatix-hub. Hub stores that access token in the session cookie, so the JWT stays small: sub, aud, issuer, expiry, and session id. Email, name, and client roles stay out of the JWT. The API reads roles from token introspection.
A password grant for external-admin@endatix.com against client endatix-hub returns a JWT whose aud is endatix-hub and endatix-app, with no resource_access. Introspection of that same token returns resource_access.endatix-hub.roles ["admin"]. Both audiences have to be in the JWT. The API checks endatix-app, and Keycloak introspection stays inactive unless endatix-hub is in aud. On Keycloak 26 the mapper switch is Add to lightweight access token (lightweight.claim). The older include.in.lightweight.access.token flag is ignored, so aud and sub disappear from the JWT and the API rejects the call.
Introduction
This guide covers:
- Enabling lightweight JWT tokens in Keycloak
- Configuring protocol mappers for token introspection
- Setting up role mappings in the Endatix API
- Testing token introspection
Why Lightweight Tokens?
Lightweight access tokens keep the JWT small:
- The Hub session cookie stays under browser size limits when a user has many client roles
- Email and name are not copied into every API request
- Role checks go through the introspection endpoint, which is where enterprise role sets belong
Lightweight Token Overview
When using lightweight tokens, certain claims cannot be removed as they are essential for security:
- exp: Expiration time - Unix timestamp when the token becomes invalid
- iat: Issued at - Unix timestamp when the token was created
- jti: JWT ID - Unique token identifier for revocation tracking
- iss: Issuer - Identifies the Keycloak realm that issued the token
- typ: Type - Declares token type (Bearer)
- azp: Authorized party - The client that the token was issued to
- sid: Session ID - Links the token to a specific Keycloak session
- scope: Scopes granted - Lists permissions or OpenID Connect scopes
Role and permission information is excluded from the lightweight token and retrieved via token introspection when needed.
Step 1: Enable Lightweight JWT Tokens in Keycloak
- Go to your realm in Keycloak Admin Console
- Click on "Clients"
- Select your client (e.g.,
endatix-hub) - Go to "Advanced settings" tab
- Find "Always use lightweight access token" and turn the switch to On
- Click "Save"
You can also use Keycloak client policies to conditionally enable lightweight tokens. See the Keycloak documentation for details.
Step 2: Keep sub and aud in the lightweight token
Keycloak 26 leaves Add to lightweight access token off on protocol mappers. Turn it on only for the claims the API reads from the JWT itself.
- Open client
endatix-hub, Client scopes, Dedicated scopes, Mappers. - For each audience mapper (
endatix-appandendatix-hub), turn Add to lightweight access token on. Leave Add to token introspection on. - Add a mapper By configuration, type Subject (sub), and turn Add to lightweight access token on. The default
submapper on thebasicclient scope does not addsubto a lightweight token, and the API requires that claim. - Leave the client-roles mapper out of the lightweight token. Turn Add to token introspection on so
resource_access.endatix-hub.rolesis returned byPOST /realms/{realm}/protocol/openid-connect/token/introspect.
The dev realm already has these mappers. A decoded access token contains sub and aud, and does not contain resource_access or email.
Step 3: Configure Client Roles
For token introspection to return role information, users must have client roles assigned:
- Go to "Clients" in Keycloak
- Click on your client (e.g.,
endatix-hub) - Go to the "Roles" tab
- Create client-specific roles. The dev realm uses
admin,creator, andrespondent. - Assign these roles to users:
- Go to "Users" → Select a user
- Go to "Role Mappings" tab
- Click "Assign role"
- Filter by your client and select the roles
- Click "Assign"
Step 4: Configure Protocol Mappers for Introspection
Ensure role mappers are configured to include data in token introspection:
- Go to "Client Scopes" → "Roles" → "Mappers" tab
- For each role mapper (e.g., "realm roles", "client roles"):
- Click on the mapper
- Ensure "Add to token introspection" is turned On
- Click "Save"
This ensures that role information is available in the introspection response even though it's excluded from the lightweight token payload.
Step 5: API Configuration
Update your appsettings.json to include the Authorization section:
{
"Endatix": {
"Auth": {
"Providers": {
"Keycloak": {
"Enabled": true,
"DefaultTenantId": 1,
"Audience": "endatix-app",
"Issuer": "http://127.0.0.1:8080/realms/endatix",
"RequireHttpsMetadata": false,
"ClientId": "endatix-hub",
"ClientSecret": "endatix-dev-hub-client-secret",
"Authorization": {
"RoleMappings": {
"admin": "Admin",
"creator": "Creator",
"respondent": "Respondent"
},
"RolesPath": "resource_access.{ClientId}.roles"
}
}
}
}
}
}
Configuration Properties
Authorization Section:
- RoleMappings: Maps Keycloak roles to Endatix application roles
- Key: Keycloak role name (as it appears in the introspection response)
- Value: Endatix role name (
Admin,Creator,Respondent, orPlatformAdmin)
- RolesPath: JSON path to roles in the token introspection response
- Use
{ClientId}placeholder which will be replaced with your actual ClientId - Example:
"resource_access.endatix-hub.roles"(if ClientId isendatix-hub)
- Use
Other Properties:
- DefaultTenantId: Default tenant ID for authenticated users (defaults to 0)
- ClientId: Your Keycloak client ID (required)
- ClientSecret: Your Keycloak client secret (required)
Step 6: Testing Token Introspection
Get an Access Token
- First, obtain a lightweight access token from Keycloak using your preferred method (OAuth flow, direct access grant, etc.).
- Call
GET /api/auth/meon the Endatix API with the token in the Authorization header.
curl --location 'https://{{ENDATIX_API_URL}}/api/auth/me' \
--header 'Authorization: Bearer {{KEYCLOAK_ACCESS_TOKEN}}'
The response should be like this:
GET /api/auth/me returns AuthorizationData. Roles are Endatix role names after RoleMappings, plus Authenticated. A user with only the Keycloak role admin looks like this. isAdmin is true because the mapped role is Admin. permissions come from those roles. There is no email field.
{
"userId": "0f6d8b28-e761-4033-8e84-2ddebecec49c",
"tenantId": 1,
"roles": ["Authenticated", "Admin"],
"permissions": ["access.authenticated", "access.apps.hub"],
"isAdmin": true,
"cachedAt": "2025-01-01T00:00:00Z",
"expiresAt": "2025-01-01T00:05:00Z",
"eTag": "1234567890"
}
Debug Tips
- Enable API logging to see introspection calls and responses
- Test introspection directly with Keycloak.
- Verify token structure by decoding the JWT (without verification) to see available claims. You can use jwt.io to decode the token.
- Check Keycloak logs for introspection endpoint activity
Security Considerations
Why Lightweight Tokens Are More Secure
- Reduced attack surface: Less information in the token means less information to exploit
- PII protection: Sensitive user data is not transmitted in every request
- Token size: Smaller tokens reduce the risk of header size limits and improve performance
Production Best Practices
- Use HTTPS: Always use HTTPS in production for token transmission
- Rotate secrets: Regularly rotate client secrets
- Monitor introspection: Log and monitor introspection endpoint usage
- Limit token lifetime: Use appropriate token expiration times
- Secure storage: Store client secrets securely (environment variables, secrets manager)
- Audit logging: Enable audit logging in Keycloak for authorization events
Next Steps
- Learn about External Authorization concepts
- Set up Session Bridge for cross-platform token exchange
- Review Keycloak Setup for basic authentication