Skip to content

client-access-type condition in Client Policy does not trigger for token request events #45740

@CXwudi

Description

@CXwudi

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

  1. Keycloak 26.4 running (Docker or standalone)
  2. A realm (e.g., demo)

Steps

  1. 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:
    
  2. Create a public client named frontend:

    • Client authentication = OFF (public client)
    • Enable Direct access grants for testing
    • Save
  3. Create a Client Profile named lightweight-token-profile:

    • Navigate to Realm SettingsClient PoliciesProfiles
    • Add executor: use-lightweight-access-token (no configuration needed)
    • Save
  4. Create a Client Policy named lightweight-token-policy:

    • Navigate to Realm SettingsClient PoliciesPolicies
    • Add condition: client-access-type
      • Configuration: type: ["public"]
      • is-negative-logic: false
    • Attach profile: lightweight-token-profile
    • Enable the policy
    • Save
  5. Verify the client toggle is OFF:

    • Go to ClientsfrontendAdvanced tab
    • Ensure Always Use Lightweight Access Token = OFF
    • (If ON, the toggle forces lightweight tokens regardless of policy)
  6. Create a test user (e.g., alice with password pass)

  7. 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"
  8. Observe the result:

    • The access_token is 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)

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

  • ClientsfrontendAdvancedAlways 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-type for 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:

  1. Manually enabling the toggle on each individual client, or
  2. Applying the policy to ALL clients using any-client condition

Both workarounds reduce flexibility compared to the expected behavior of the client-access-type condition.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions