adorsys / keycloak-config-cli

Import YAML/JSON-formatted configuration files into Keycloak - Configuration as Code for Keycloak.
Apache License 2.0
703 stars 129 forks source link

Add mention of basic scope handling to migration guide #1073

Open thomasdarimont opened 5 days ago

thomasdarimont commented 5 days ago

Problem Statement

With the introduction of the dedicated "basic" scope in Keycloak, existing realm configurations with custom clients might now produce access tokens that don't contain the sub claim anymore. This is because the new basic scope that emits those claims might be removed by an explicit defaultClientScopes configuration.

The Keycloak 25 migration documentation documents that the basic scope is automatically added to existing clients during realm migration, however some client configurations with a defaultClientScopes configuration might lack the basic scope configuration. In that case keycloak-config-cli will remove the basic scope again.

Proposed Solution

Add a short note to the migration guide about the need to add the basic scope explicitly when a client configuration uses defaultClientScopes.

A workaround is to configure the basic scope explicitly via defaultClientScopes

    defaultClientScopes:
      - "basic"

Environment

Additional information

I think this is actually a regression in Keycloak, but some folks might blame keycloak-config-cli here.

Example of a previously working client definition, which will produce access tokens with the sub claim:

  - clientId: app-greetme
    protocol: openid-connect
    name: Acme Greet Me
    description: "App Greet Me Description"
    enabled: true
    publicClient: true
    standardFlowEnabled: true
    directAccessGrantsEnabled: false
    # Show client in account-console
    alwaysDisplayInConsole: true
    serviceAccountsEnabled: false
    #    attributes: { }
    fullScopeAllowed: false
    rootUrl: "$(env:APPS_FRONTEND_URL_GREETME:https://localhost:9443/apps/greet-me)"
    baseUrl: "/?realm=acme-internal&scope=openid"
    adminUrl: ""
    redirectUris:
      - "/*"
    webOrigins:
      - "+"
    defaultClientScopes:
      - "email"
    optionalClientScopes:
      - "phone"
      - "name"
      - "acme.api"
      - "address"
    attributes:
      "pkce.code.challenge.method": "S256"
      "post.logout.redirect.uris": "+"

Working client configuration:

  - clientId: app-greetme
    protocol: openid-connect
    name: Acme Greet Me
    description: "App Greet Me Description"
    enabled: true
    publicClient: true
    standardFlowEnabled: true
    directAccessGrantsEnabled: false
    # Show client in account-console
    alwaysDisplayInConsole: true
    serviceAccountsEnabled: false
    #    attributes: { }
    fullScopeAllowed: false
    rootUrl: "$(env:APPS_FRONTEND_URL_GREETME:https://localhost:9443/apps/greet-me)"
    baseUrl: "/?realm=acme-internal&scope=openid"
    adminUrl: ""
    redirectUris:
      - "/*"
    webOrigins:
      - "+"
    defaultClientScopes:
      - "default"
      - "email"
    optionalClientScopes:
      - "phone"
      - "name"
      - "acme.api"
      - "address"
    attributes:
      "pkce.code.challenge.method": "S256"
      "post.logout.redirect.uris": "+"

Acceptance Criteria

No response

thomasdarimont commented 5 days ago

CC @francis-pouatcha @jonasvoelcker

jonasvoelcker commented 3 days ago

Hi @thomasdarimont,

I guess it is also necessary to define the basic-scope:

    "clientScopes": [
        {
            "name": "basic",
            "description": "OpenID Connect scope for add all basic claims to the token",
            "protocol": "openid-connect",
            "attributes": {
                "include.in.token.scope": "false",
                "display.on.consent.screen": "false"
            },
            "protocolMappers": [
                {
                    "name": "sub",
                    "protocol": "openid-connect",
                    "protocolMapper": "oidc-sub-mapper",
                    "consentRequired": false,
                    "config": {
                        "access.token.claim": "true",
                        "introspection.token.claim": "true"
                    }
                },
                {
                    "name": "auth_time",
                    "protocol": "openid-connect",
                    "protocolMapper": "oidc-usersessionmodel-note-mapper",
                    "consentRequired": false,
                    "config": {
                        "user.session.note": "AUTH_TIME",
                        "id.token.claim": "true",
                        "introspection.token.claim": "true",
                        "access.token.claim": "true",
                        "claim.name": "auth_time",
                        "jsonType.label": "long"
                    }
                }
            ]
        },
        ...
    ]