diff --git a/services/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java b/services/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java index 07c38d1f86..b37d7f3953 100755 --- a/services/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java +++ b/services/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java @@ -438,14 +438,16 @@ public abstract class AbstractOAuth2IdentityProvider createProviderClients() { + List clientsRepList = super.createProviderClients(); + log.info("Update provider clients to accept JWT authentication"); + for (ClientRepresentation client : clientsRepList) { + if (client.getAttributes() == null) { + client.setAttributes(new HashMap()); + } + client.setClientAuthenticatorType(JWTClientSecretAuthenticator.PROVIDER_ID); + client.setSecret(clientSecret); + client.getAttributes().put(OIDCConfigAttributes.TOKEN_ENDPOINT_AUTH_SIGNING_ALG, signAlg); + } + return clientsRepList; + } + + @Override + public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSyncMode syncMode) { + IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID); + Map config = idp.getConfig(); + applyDefaultConfiguration(config, syncMode); + config.put("clientAuthMethod", OIDCLoginProtocol.CLIENT_SECRET_JWT); + config.put("clientSecret", clientSecret); + config.put("clientAssertionSigningAlg", signAlg); + return idp; + } + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java new file mode 100644 index 0000000000..47812be1d4 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerPrivateKeyJwtCustomSignAlgTest.java @@ -0,0 +1,92 @@ +package org.keycloak.testsuite.broker; + +import org.keycloak.admin.client.Keycloak; +import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; +import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.crypto.Algorithm; +import org.keycloak.keys.GeneratedEcdsaKeyProviderFactory; +import org.keycloak.keys.KeyProvider; +import org.keycloak.models.IdentityProviderSyncMode; +import org.keycloak.protocol.oidc.OIDCConfigAttributes; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ComponentRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testsuite.util.TokenSignatureUtil; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; +import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID; +import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_CONS_NAME; +import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider; +import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot; + +public class KcOidcBrokerPrivateKeyJwtCustomSignAlgTest extends AbstractBrokerTest { + + @Override + protected BrokerConfiguration getBrokerConfiguration() { + return new KcOidcBrokerConfigurationWithJWTAuthentication(); + } + + private class KcOidcBrokerConfigurationWithJWTAuthentication extends KcOidcBrokerConfiguration { + + String signAlg = Algorithm.ES256; + + @Override + public List createProviderClients() { + List clientsRepList = super.createProviderClients(); + log.info("Update provider clients to accept JWT authentication"); + for (ClientRepresentation client: clientsRepList) { + client.setClientAuthenticatorType(JWTClientAuthenticator.PROVIDER_ID); + if (client.getAttributes() == null) { + client.setAttributes(new HashMap()); + } + client.getAttributes().put(OIDCConfigAttributes.TOKEN_ENDPOINT_AUTH_SIGNING_ALG, signAlg); + client.getAttributes().put(OIDCConfigAttributes.USE_JWKS_URL, "true"); + client.getAttributes().put(OIDCConfigAttributes.JWKS_URL, getConsumerRoot() + + "/auth/realms/" + REALM_CONS_NAME + "/protocol/openid-connect/certs"); + } + return clientsRepList; + } + + @Override + public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSyncMode syncMode) { + generateEcdsaKeyProvider("valid", signAlg, REALM_CONS_NAME, adminClient); + IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID); + Map config = idp.getConfig(); + applyDefaultConfiguration(config, syncMode); + config.put("clientSecret", null); + config.put("clientAuthMethod", OIDCLoginProtocol.PRIVATE_KEY_JWT); + config.put("clientAssertionSigningAlg", signAlg); + return idp; + } + + private void generateEcdsaKeyProvider(String name, String alg, String realmName, Keycloak adminClient) { + ComponentRepresentation rep = createRep(name, + adminClient.realm(realmName).toRepresentation().getId(), GeneratedEcdsaKeyProviderFactory.ID); + long priority = System.currentTimeMillis(); + rep.getConfig().putSingle("priority", Long.toString(priority)); + rep.getConfig().putSingle("active", "true"); + rep.getConfig().putSingle("enabled", "true"); + rep.getConfig().putSingle("ecdsaEllipticCurveKey", + TokenSignatureUtil.convertAlgorithmToECDomainParamNistRep(alg)); + Response response = adminClient.realm(realmName).components().add(rep); + response.close(); + } + + protected ComponentRepresentation createRep(String name, String realmId, String providerId) { + ComponentRepresentation rep = new ComponentRepresentation(); + rep.setName(name); + rep.setParentId(realmId); + rep.setProviderId(providerId); + rep.setProviderType(KeyProvider.class.getName()); + rep.setConfig(new MultivaluedHashMap<>()); + return rep; + } + } +} \ No newline at end of file diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ja.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ja.properties index 1333402a01..47288ffe7b 100644 --- a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ja.properties +++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ja.properties @@ -629,6 +629,8 @@ client-secret=クライアント・シークレット show-secret=シークレットを表示する hide-secret=シークレットを隠す client-secret.tooltip=アイデンティティー・プロバイダーで登録されているクライアントまたはクライアント・シークレットを設定します。このフィールドは、ボールトから値を取得できます。${vault.ID}形式を使用します。 +client-assertion-signing-algorithm=クライアントアサーション署名アルゴリズム +client-assertion-signing-algorithm.tooltip=クライアント認証でJWTアサーションを利用するときの署名アルゴリズム。クライアント認証が 秘密鍵で署名されたJWT もしくは JWTでクライアント・シークレット の場合に設定します。アルゴリズムの指定をしなかった場合、 秘密鍵で署名されたJWT ではRS256 JWTでクライアント・シークレット ではHS256のアルゴリズムが使用されます。 issuer=発行者(Issuer) issuer.tooltip=レスポンス内の発行者の識別子(Issuer Identifier)を設定します。未設定の場合は、検証は実行されません。 default-scopes=デフォルト・スコープ diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index 50375a7307..0f2bef42ca 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -652,6 +652,8 @@ client-auth.client_secret_jwt=Client secret as jwt client-auth.private_key_jwt=JWT signed with private key identity-provider.client-id.tooltip=The client or client identifier registered within the identity provider. client-secret=Client Secret +client-assertion-signing-algorithm=Client Assertion Signature Algorithm +client-assertion-signing-algorithm.tooltip=Signature algorithm to create JWT assertion as client authentication. In the case of JWT signed with private key or Client secret as jwt, it is required. If no algorithm is specified, the following algorithm is adapted. RS256 is adapted in the case of JWT signed with private key. HS256 is adapted in the case of Client secret as jwt. show-secret=Show secret hide-secret=Hide secret client-secret.tooltip=The client or client secret registered within the identity provider. This field is able to obtain its value from vault, use ${vault.ID} format. diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html index b2cc785430..e9b28e288a 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html @@ -210,6 +210,18 @@ {{:: 'client-secret.tooltip' | translate}} + +
+ +
+ +
+ {{:: 'client-assertion-signing-algorithm.tooltip' | translate}}