From 69c8b78e98a33da23f85792ca7edc077dd3bc09d Mon Sep 17 00:00:00 2001 From: Konstantinos Georgilakis Date: Wed, 23 Mar 2022 12:46:31 +0200 Subject: [PATCH] Include 'urn:ietf:params:oauth:grant-type:token-exchange' in grant_types_supported field of Keycloak OP metadata, if token-exchange is enabled closes #10888 --- .../protocol/oidc/OIDCWellKnownProvider.java | 13 +++++++++---- .../oidc/OIDCWellKnownProviderTest.java | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java index 7fd982b2949e..930917fc9ef1 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java @@ -22,6 +22,7 @@ import org.keycloak.authentication.ClientAuthenticator; import org.keycloak.authentication.ClientAuthenticatorFactory; import org.keycloak.authentication.authenticators.util.LoAUtil; +import org.keycloak.common.Profile; import org.keycloak.crypto.CekManagementProvider; import org.keycloak.crypto.ClientSignatureVerifierProvider; import org.keycloak.crypto.ContentEncryptionProvider; @@ -70,10 +71,7 @@ */ public class OIDCWellKnownProvider implements WellKnownProvider { - public static final List DEFAULT_GRANT_TYPES_SUPPORTED = list(OAuth2Constants.AUTHORIZATION_CODE, - OAuth2Constants.IMPLICIT, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD, OAuth2Constants.CLIENT_CREDENTIALS, - OAuth2Constants.DEVICE_CODE_GRANT_TYPE, - OAuth2Constants.CIBA_GRANT_TYPE); + public final List DEFAULT_GRANT_TYPES_SUPPORTED; public static final List DEFAULT_RESPONSE_TYPES_SUPPORTED = list(OAuth2Constants.CODE, OIDCResponseType.NONE, OIDCResponseType.ID_TOKEN, OIDCResponseType.TOKEN, "id_token token", "code id_token", "code token", "code id_token token"); @@ -100,6 +98,13 @@ public OIDCWellKnownProvider(KeycloakSession session) { } public OIDCWellKnownProvider(KeycloakSession session, Map openidConfigOverride, boolean includeClientScopes) { + DEFAULT_GRANT_TYPES_SUPPORTED = Stream.of(OAuth2Constants.AUTHORIZATION_CODE, + OAuth2Constants.IMPLICIT, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD, OAuth2Constants.CLIENT_CREDENTIALS, + OAuth2Constants.DEVICE_CODE_GRANT_TYPE, + OAuth2Constants.CIBA_GRANT_TYPE).collect(Collectors.toList()); + if (Profile.isFeatureEnabled(Profile.Feature.TOKEN_EXCHANGE)) { + DEFAULT_GRANT_TYPES_SUPPORTED.add(OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE); + } this.session = session; this.openidConfigOverride = openidConfigOverride; this.includeClientScopes = includeClientScopes; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java index 68edb1aea303..af15a52db1aa 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java @@ -26,6 +26,7 @@ import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.broker.provider.util.SimpleHttp; +import org.keycloak.common.Profile; import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jwe.JWEConstants; import org.keycloak.jose.jwk.JSONWebKeySet; @@ -46,6 +47,7 @@ import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.admin.AbstractAdminTest; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude; +import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.forms.BrowserFlowTest; import org.keycloak.testsuite.forms.LevelOfAssuranceFlowTest; import org.keycloak.testsuite.util.AdminClientUtil; @@ -133,6 +135,7 @@ public void testDiscovery() { // Support standard + implicit + hybrid flow assertContains(oidcConfig.getResponseTypesSupported(), OAuth2Constants.CODE, OIDCResponseType.ID_TOKEN, "id_token token", "code id_token", "code token", "code id_token token"); + assertEquals(oidcConfig.getGrantTypesSupported().size(),7); assertContains(oidcConfig.getGrantTypesSupported(), OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.IMPLICIT, OAuth2Constants.DEVICE_CODE_GRANT_TYPE); assertContains(oidcConfig.getResponseModesSupported(), "query", "fragment", "form_post", "jwt", "query.jwt", "fragment.jwt", "form_post.jwt"); @@ -382,6 +385,19 @@ public void testDefaultProviderCustomizations() throws IOException { } } + @Test + @EnableFeature(value = Profile.Feature.TOKEN_EXCHANGE, skipRestart = true) + public void testGrantTypesSupportedWithTokenExchange() throws IOException { + Client client = AdminClientUtil.createResteasyClient(); + try { + OIDCConfigurationRepresentation oidcConfig = getOIDCDiscoveryRepresentation(client, OAuthClient.AUTH_SERVER_ROOT); + assertEquals(oidcConfig.getGrantTypesSupported().size(),8); + assertContains(oidcConfig.getGrantTypesSupported(), OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE); + } finally { + client.close(); + } + } + private void assertScopesSupportedMatchesWithRealm(OIDCConfigurationRepresentation oidcConfig) { Assert.assertNames(oidcConfig.getScopesSupported(), OAuth2Constants.SCOPE_OPENID, OAuth2Constants.OFFLINE_ACCESS, OAuth2Constants.SCOPE_PROFILE, OAuth2Constants.SCOPE_EMAIL, OAuth2Constants.SCOPE_PHONE, OAuth2Constants.SCOPE_ADDRESS, OIDCLoginProtocolFactory.ACR_SCOPE,