keycloak-config-cli: Creation of realm with client_credentials authentication flow fails with 403 Forbidden

Describe the bug Creation of a new realm fails with HTTP 403 Forbidden if client_credentials authentication flow with ClientId/ClientSecret is used (although the realm actually got created).

To Reproduce

  • Create client my-cli at master realm (with user based authentication) [click to expand]

    The client gets the service account role admin which enables management of realms.

    JSON (realm-master.json):

    {
      "realm": "master",
      "users": [
        {
          "username": "service-account-my-cli",
          "enabled": true,
          "serviceAccountClientId": "my-cli",
          "realmRoles": [
            "uma_authorization",
            "offline_access",
            "admin"
          ],
          "clientRoles": {
            "account": [
              "view-profile",
              "manage-account"
            ]
          }
        }
      ],
      "clients": [
        {
          "clientId": "my-cli",
          "enabled": true,
          "clientAuthenticatorType": "client-secret",
          "secret": "my-secret",
          "bearerOnly": false,
          "consentRequired": false,
          "standardFlowEnabled": false,
          "implicitFlowEnabled": false,
          "directAccessGrantsEnabled": false,
          "serviceAccountsEnabled": true,
          "publicClient": false,
          "frontchannelLogout": false,
          "protocol": "openid-connect",
          "protocolMappers": [
            {
              "name": "security-admin-console-audience-mapper",
              "protocol": "openid-connect",
              "protocolMapper": "oidc-audience-mapper",
              "consentRequired": false,
              "config": {
                "included.client.audience": "security-admin-console",
                "id.token.claim": "false",
                "access.token.claim": "true"
              }
            }
          ]
        }
      ],
      "keycloakVersion": "12.0.4"
    }
    

    CMD:

    docker run -it --rm -e KEYCLOAK_URL=http://localhost:8080/auth -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=<my-admin-pw> -e KEYCLOAK_GRANTTYPE=password -e IMPORT_PATH=/config/realm-master.json -e SPRING_PROFILES_INCLUDE=debug -v ./config:/config adorsys/keycloak-config-cli:v3.1.0-12.0.3
    

    based on https://www.keycloak.org/docs/latest/server_development/#authenticate-with-a-service-account

  • Try to create realm mynode using client_credentials authentication flow [click to expand]

    JSON (realm-node.json):

    {
      "realm": "mynode",
      "displayName": "mynode"
    }
    

    CMD:

    docker run -it --rm -e KEYCLOAK_URL=http://localhost:8080/auth -e KEYCLOAK_CLIENTID=my-cli -e KEYCLOAK_CLIENTSECRET=my-secret -e KEYCLOAK_GRANTTYPE=client_credentials -e IMPORT_PATH=/config/realm-node.json -e SPRING_PROFILES_INCLUDE=debug -v ./config/:/config adorsys/keycloak-config-cli:v3.1.0-12.0.3
    
  • Causes the following error:

     INFO 1 [main] d.a.k.config.KeycloakConfigApplication   : Starting KeycloakConfigApplication v3.1.0 using Java 11.0.10 on e3b541095cad with PID 1 (/opt/keycloak-config-cli.jar started by ? in /)
     INFO 1 [main] d.a.k.config.KeycloakConfigApplication   : The following profiles are active: debug
     INFO 1 [main] d.a.k.config.KeycloakConfigApplication   : Started KeycloakConfigApplication in 1.272 seconds (JVM running for 1.696)
     INFO 1 [main] d.a.k.c.provider.KeycloakImportProvider  : Importing file '/config/realm-node.json'
     INFO 1 [main] d.a.k.config.provider.KeycloakProvider   : Wait 60 seconds until http://localhost:8080/auth is available ...
    DEBUG 1 [main] d.a.k.config.service.RealmImportService  : Creating realm 'mynode' ...
    ERROR 1 [main] d.a.k.config.KeycloakConfigRunner        : HTTP 403 Forbidden
     INFO 1 [main] d.a.k.config.KeycloakConfigRunner        : keycloak-config-cli running in 00:02.024.
    
    Stack-Trace [click to expand]
    ERROR 1 [main] o.s.boot.SpringApplication               : Application run failed
    java.lang.IllegalStateException: Failed to execute CommandLineRunner
            at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:807) ~[spring-boot-2.4.3.jar!/:2.4.3]
            at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:788) ~[spring-boot-2.4.3.jar!/:2.4.3]
            at org.springframework.boot.SpringApplication.run(SpringApplication.java:333) ~[spring-boot-2.4.3.jar!/:2.4.3]
            at org.springframework.boot.SpringApplication.run(SpringApplication.java:1311) ~[spring-boot-2.4.3.jar!/:2.4.3]
            at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) ~[spring-boot-2.4.3.jar!/:2.4.3]
            at de.adorsys.keycloak.config.KeycloakConfigApplication.main(KeycloakConfigApplication.java:35) ~[classes!/:3.1.0]
            at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
            at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:na]
            at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:na]
            at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
            at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[keycloak-config-cli.jar:3.1.0]
            at org.springframework.boot.loader.Launcher.launch(Launcher.java:107) ~[keycloak-config-cli.jar:3.1.0]
            at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[keycloak-config-cli.jar:3.1.0]
            at org.springframework.boot.loader.PropertiesLauncher.main(PropertiesLauncher.java:467) ~[keycloak-config-cli.jar:3.1.0]
    Caused by: javax.ws.rs.ForbiddenException: HTTP 403 Forbidden
            at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.handleErrorStatus(ClientInvocation.java:266) ~[resteasy-client-4.6.0.Final.jar!/:4.6.0.Final]
            at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:238) ~[resteasy-client-4.6.0.Final.jar!/:4.6.0.Final]
            at org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.BodyEntityExtractor.extractEntity(BodyEntityExtractor.java:64) ~[resteasy-client-4.6.0.Final.jar!/:4.6.0.Final]
            at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:154) ~[resteasy-client-4.6.0.Final.jar!/:4.6.0.Final]
            at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:115) ~[resteasy-client-4.6.0.Final.jar!/:4.6.0.Final]
            at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:76) ~[resteasy-client-4.6.0.Final.jar!/:4.6.0.Final]
            at com.sun.proxy.$Proxy87.toRepresentation(Unknown Source) ~[na:na]
            at de.adorsys.keycloak.config.repository.RealmRepository.get(RealmRepository.java:69) ~[classes!/:3.1.0]
            at de.adorsys.keycloak.config.repository.StateRepository.retrieveCustomAttributes(StateRepository.java:118) ~[classes!/:3.1.0]
            at de.adorsys.keycloak.config.repository.StateRepository.loadCustomAttributes(StateRepository.java:68) ~[classes!/:3.1.0]
            at de.adorsys.keycloak.config.service.state.StateService.loadState(StateService.java:53) ~[classes!/:3.1.0]
            at de.adorsys.keycloak.config.service.RealmImportService.configureRealm(RealmImportService.java:171) ~[classes!/:3.1.0]
            at de.adorsys.keycloak.config.service.RealmImportService.createRealm(RealmImportService.java:158) ~[classes!/:3.1.0]
            at de.adorsys.keycloak.config.service.RealmImportService.doImport(RealmImportService.java:134) ~[classes!/:3.1.0]
            at de.adorsys.keycloak.config.KeycloakConfigRunner.run(KeycloakConfigRunner.java:70) ~[classes!/:3.1.0]
            at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:804) ~[spring-boot-2.4.3.jar!/:2.4.3]
            ... 13 common frames omitted
    
    The realm `mynode` was nevertheless created but (no further configuration would occur due to the error).
  • The log from keycloak shows no authentication problems:

    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) new JtaTransactionWrapper
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) was existing? false
    DEBUG [org.keycloak.authentication.AuthenticationProcessor] (default task-5) AUTHENTICATE CLIENT
    DEBUG [org.keycloak.authentication.ClientAuthenticationFlow] (default task-5) client authenticator: client-secret
    DEBUG [org.keycloak.authentication.ClientAuthenticationFlow] (default task-5) client authenticator SUCCESS: client-secret
    DEBUG [org.keycloak.authentication.ClientAuthenticationFlow] (default task-5) Client my-cli authenticated by client-secret
    DEBUG [org.keycloak.services.managers.AuthenticationSessionManager] (default task-5) Removing authSession 'a3147fc7-6d89-489e-97bb-001ba846226e'. Expire restart cookie: true
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) JtaTransactionWrapper  commit
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) JtaTransactionWrapper end
    DEBUG [org.keycloak.events] (default task-5) type=CLIENT_LOGIN, realmId=master, clientId=my-cli, userId=593bf4b3-be03-42f0-9668-28f3da5d874a, ipAddress=192.168.48.1, token_id=fc1e7fa0-ecd3-4f5e-a89f-261db53c7b4c, grant_type=client_credentials, scope='profile email', client_auth_method=client-secret, username=service-account-my-cli, authSessionParentId=a3147fc7-6d89-489e-97bb-001ba846226e, authSessionTabId=til6nfT4y9w
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) new JtaTransactionWrapper
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) was existing? false
    DEBUG [org.keycloak.services.resources.admin.AdminRoot] (default task-5) authenticated admin access for: service-account-my-cli
    DEBUG [org.keycloak.saml.common] (default task-5) Using logger implementation: org.keycloak.saml.common.DefaultPicketLinkLogger
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) JtaTransactionWrapper  commit
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) JtaTransactionWrapper end
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) new JtaTransactionWrapper
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) was existing? false
    DEBUG [org.keycloak.services.resources.admin.AdminRoot] (default task-5) authenticated admin access for: service-account-my-cli
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) JtaTransactionWrapper rollback
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) JtaTransactionWrapper end
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) new JtaTransactionWrapper
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) was existing? false
    DEBUG [org.keycloak.services.resources.admin.AdminRoot] (default task-5) authenticated admin access for: service-account-my-cli
    DEBUG [org.keycloak.services.resources.admin.RealmsAdminResource] (default task-5) importRealm: mynode
    DEBUG [org.keycloak.keys.GeneratedRsaKeyProviderFactory] (default task-5) Generated keys for mynode
    DEBUG [org.keycloak.keys.GeneratedHmacKeyProviderFactory] (default task-5) Generated secret for mynode
    DEBUG [org.keycloak.keys.GeneratedAesKeyProviderFactory] (default task-5) Generated secret for mynode
    DEBUG [org.keycloak.services.resources.admin.RealmsAdminResource] (default task-5) imported realm success, sending back: http://localhost:8080/auth/admin/realms/mynode
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) JtaTransactionWrapper  commit
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) JtaTransactionWrapper end
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) new JtaTransactionWrapper
    DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (default task-5) was existing? false
    DEBUG [org.keycloak.services.resources.admin.AdminRoot] (default task-5) authenticated admin access for: service-account-my-cli
    

Expected behavior The realm should be created without any errors and further configuration of the realm (e.g. creation of clients) should occur.

Possible issue? Could it be that the CLI needs to refresh the access-token after the creation of the realm? Since otherwise there are no claims for the new realm mynode-realm on the token. See comparison of a token manually issued before and after creation of the realm. This theory would fit if the RealmRepository.get() method triggers some requests. However I couldn’t see any logs on the keycloak server regarding 403 forbidden.

Access token before creation of new realm [click to expand]
{
  "exp": 1614968632,
  "iat": 1614968572,
  "jti": "a5e103b1-82ec-4cad-93c9-c1de835e79ff",
  "iss": "http://localhost:8080/auth/realms/master",
  "aud": [
    "security-admin-console",
    "master-realm",
    "account"
  ],
  "sub": "5255a1fd-0f3c-4b28-baf9-4bfc11f4d7fc",
  "typ": "Bearer",
  "azp": "my-cli",
  "acr": "1",
  "realm_access": {
    "roles": [
      "create-realm",
      "offline_access",
      "admin",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "master-realm": {
      "roles": [
        "view-identity-providers",
        "view-realm",
        "manage-identity-providers",
        "impersonation",
        "create-client",
        "manage-users",
        "query-realms",
        "view-authorization",
        "query-clients",
        "query-users",
        "manage-events",
        "manage-realm",
        "view-events",
        "view-users",
        "view-clients",
        "manage-authorization",
        "manage-clients",
        "query-groups"
      ]
    },
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "email profile",
  "clientHost": "192.168.80.1",
  "email_verified": false,
  "clientId": "my-cli",
  "preferred_username": "service-account-my-cli",
  "clientAddress": "192.168.80.1"
}
Access token after creation of new realm [click to expand]
{
"exp": 1614968495,
"iat": 1614968435,
"jti": "899b7864-12fb-4461-aa72-0e0383575260",
"iss": "http://localhost:8080/auth/realms/master",
"aud": [
  "security-admin-console",
  "mynode-realm",
  "master-realm",
  "account"
],
"sub": "593bf4b3-be03-42f0-9668-28f3da5d874a",
"typ": "Bearer",
"azp": "my-cli",
"acr": "1",
"realm_access": {
  "roles": [
    "create-realm",
    "offline_access",
    "admin",
    "uma_authorization"
  ]
},
"resource_access": {
  "mynode-realm": {
    "roles": [
      "view-identity-providers",
      "view-realm",
      "manage-identity-providers",
      "impersonation",
      "create-client",
      "manage-users",
      "query-realms",
      "view-authorization",
      "query-clients",
      "query-users",
      "manage-events",
      "manage-realm",
      "view-events",
      "view-users",
      "view-clients",
      "manage-authorization",
      "manage-clients",
      "query-groups"
    ]
  },
  "master-realm": {
    "roles": [
      "view-realm",
      "view-identity-providers",
      "manage-identity-providers",
      "impersonation",
      "create-client",
      "manage-users",
      "query-realms",
      "view-authorization",
      "query-clients",
      "query-users",
      "manage-events",
      "manage-realm",
      "view-events",
      "view-users",
      "view-clients",
      "manage-authorization",
      "manage-clients",
      "query-groups"
    ]
  },
  "account": {
    "roles": [
      "manage-account",
      "manage-account-links",
      "view-profile"
    ]
  }
},
"scope": "profile email",
"clientHost": "192.168.48.1",
"email_verified": false,
"clientId": "my-cli",
"preferred_username": "service-account-my-cli",
"clientAddress": "192.168.48.1"
}

Only workaround we know so far is to use a kind of “service”-user instead of the client (since everything works with user authentication).

Environment (please complete the following information):

  • Keycloak Version: 12.0.4
  • keycloak-config-cli Version: v3.1.0-12.0.3
  • Java Version: 11.0.10+9

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 22 (11 by maintainers)

Most upvoted comments

I guess it’s something like this

  keycloak-config-cli:
    container_name: keycloak-config-cli
    image: quay.io/adorsys/keycloak-config-cli:v4.5.0-15.1.1
...
    network_mode: "service:keycloak"

and remove the networks from that service completly.

Looks like you found already a alternative solution.

Checkout compose network_mode

https://docs.docker.com/compose/compose-file/compose-file-v3/#network_mode

You can configure that the keycloak-config-cli container will join the keycloak network namespace. If both containers share the same namespace, then you can reach keycloak via localhost.

I appreciate the detailed bug description. It helps a lot to reproduce the case and find the error quickly.

Could it be that the CLI needs to refresh the access-token after the creation of the realm?

I tried this and this resolves the error. Godlike hint.