-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Description
Before reporting an issue
- I have read and understood the above terms for submitting issues, and I understand that my issue may be closed without action if I do not follow them.
Area
core
Describe the bug
When creating a Client Policy with a client-access-type condition set to public and attaching a profile with use-lightweight-access-token executor, the policy is not applied during token requests. The client receives a full JWT instead of a lightweight access token.
Version
26.4 (Docker image: quay.io/keycloak/keycloak:26.4)
Regression
- The issue is a regression
Expected behavior
A public client (publicClient: true) should match the client-access-type condition with type ["public"] during token requests (TOKEN_REQUEST event), and the attached use-lightweight-access-token executor should produce a lightweight/opaque access token.
Actual behavior
The client-access-type condition does not match during token requests. The client receives a full JWT with all claims (name, email, roles, etc.) instead of a lightweight token.
How to Reproduce?
Prerequisites
- Keycloak 26.4 running (Docker or standalone)
- A realm (e.g.,
demo)
Steps
-
Create the following setup in Keycloak:
services: keycloak: image: quay.io/keycloak/keycloak:26.4 command: > start-dev --http-port=8080 environment: KC_BOOTSTRAP_ADMIN_USERNAME: admin KC_BOOTSTRAP_ADMIN_PASSWORD: admin KC_DB: postgres KC_DB_URL: jdbc:postgresql://keycloak-db:5432/keycloak KC_DB_USERNAME: keycloak KC_DB_PASSWORD: keycloak KC_HOSTNAME: <http://localhost:8081> KC_HOSTNAME_STRICT: false KC_HOSTNAME_BACKCHANNEL_DYNAMIC: true ports: - "8081:8080" depends_on: - keycloak-db keycloak-db: image: postgres:18-alpine environment: POSTGRES_DB: keycloak POSTGRES_USER: keycloak POSTGRES_PASSWORD: keycloak volumes: - keycloak-db-data:/var/lib/postgresql volumes: keycloak-db-data:
-
Create a public client named
frontend:- Client authentication = OFF (public client)
- Enable
Direct access grantsfor testing - Save
-
Create a Client Profile named
lightweight-token-profile:- Navigate to
Realm Settings→Client Policies→Profiles - Add executor:
use-lightweight-access-token(no configuration needed) - Save
- Navigate to
-
Create a Client Policy named
lightweight-token-policy:- Navigate to
Realm Settings→Client Policies→Policies - Add condition:
client-access-type- Configuration:
type: ["public"] is-negative-logic: false
- Configuration:
- Attach profile:
lightweight-token-profile - Enable the policy
- Save
- Navigate to
-
Verify the client toggle is OFF:
- Go to
Clients→frontend→Advancedtab - Ensure
Always Use Lightweight Access Token= OFF - (If ON, the toggle forces lightweight tokens regardless of policy)
- Go to
-
Create a test user (e.g.,
alicewith passwordpass) -
Request a token:
curl -X POST http://localhost:8081/realms/demo/protocol/openid-connect/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password&client_id=frontend&username=alice&password=pass"
-
Observe the result:
- The
access_tokenis a full JWT (decode it at jwt.io) - Contains all claims:
name,email,realm_access.roles, etc. - Expected: Should be a short opaque string (lightweight token)
- The
Anything else?
Test Results Comparison
| Condition | Works? | Notes |
|---|---|---|
any-client |
✅ Yes | Produces lightweight tokens for all clients |
grant-type (password) |
✅ Yes | Produces lightweight tokens for specified grant types |
| Simple toggle on client | ✅ Yes | Produces lightweight tokens when enabled |
client-access-type (public) |
❌ NO | Does NOT produce lightweight tokens |
Workarounds
Option 1: Use the simple toggle directly on the client
Clients→frontend→Advanced→Always Use Lightweight Access Token= ON
Option 2: Use any-client or grant-type conditions instead
- Works correctly but applies to all clients or all clients using specific grant types
- Cannot target specific client types (public vs confidential)
Environment Details
- Keycloak version: 26.4
- Docker image:
quay.io/keycloak/keycloak:26.4 - Database: Default H2 (also tested with PostgreSQL - same behavior)
- OS: Windows 10 (also tested on Linux - same behavior)
Source Code Analysis
The ClientAccessTypeCondition.java source code shows it should handle TOKEN_REQUEST events:
@Override
public boolean isMatch(ClientPolicyContext context, ClientAccessTypeCondition.Configuration configuration) {
// ...
switch (context.getEvent()) {
case TOKEN_REQUEST:
case TOKEN_REFRESH:
// ... other events
}
}However, during actual token requests, the condition does not trigger.
Policy JSON Export
{
"policies": [
{
"name": "lightweight-token-policy",
"description": "",
"enabled": true,
"conditions": [
{
"condition": "client-access-type",
"configuration": {
"is-negative-logic": false,
"type": ["public"]
}
}
],
"profiles": ["lightweight-token-profile"]
}
]
}Profile JSON Export
{
"profiles": [
{
"name": "lightweight-token-profile",
"description": "",
"executors": [
{
"executor": "use-lightweight-access-token",
"configuration": {}
}
]
}
]
}Related Issues
- #12295 - Similar issue with
client-access-typefor client registration events (REGISTER/UPDATE), but this bug is specific to token request events (TOKEN_REQUEST).
Impact
This bug prevents fine-grained control over which clients receive lightweight tokens based on their access type (public vs confidential). The workaround requires either:
- Manually enabling the toggle on each individual client, or
- Applying the policy to ALL clients using
any-clientcondition
Both workarounds reduce flexibility compared to the expected behavior of the client-access-type condition.