KEYCLOAK-16679 Add algorithm settings for client assertion signature in OIDC identity broker
This commit is contained in:
parent
c4bf8ecdf0
commit
b83064b142
7 changed files with 179 additions and 3 deletions
|
@ -438,14 +438,16 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
|||
if (getConfig().getClientAuthMethod().equals(OIDCLoginProtocol.CLIENT_SECRET_JWT)) {
|
||||
try (VaultStringSecret vaultStringSecret = session.vault().getStringSecret(getConfig().getClientSecret())) {
|
||||
KeyWrapper key = new KeyWrapper();
|
||||
key.setAlgorithm(Algorithm.HS256);
|
||||
String alg = getConfig().getClientAssertionSigningAlg() != null ? getConfig().getClientAssertionSigningAlg() : Algorithm.HS256;
|
||||
key.setAlgorithm(alg);
|
||||
byte[] decodedSecret = vaultStringSecret.get().orElse(getConfig().getClientSecret()).getBytes();
|
||||
SecretKey secret = new SecretKeySpec(decodedSecret, 0, decodedSecret.length, Algorithm.HS256);
|
||||
SecretKey secret = new SecretKeySpec(decodedSecret, 0, decodedSecret.length, alg);
|
||||
key.setSecretKey(secret);
|
||||
return new MacSignatureSignerContext(key);
|
||||
}
|
||||
}
|
||||
return new AsymmetricSignatureProvider(session, Algorithm.RS256).signer();
|
||||
String alg = getConfig().getClientAssertionSigningAlg() != null ? getConfig().getClientAssertionSigningAlg() : Algorithm.RS256;
|
||||
return new AsymmetricSignatureProvider(session, alg).signer();
|
||||
}
|
||||
|
||||
protected class Endpoint {
|
||||
|
|
|
@ -147,6 +147,14 @@ public class OAuth2IdentityProviderConfig extends IdentityProviderModel {
|
|||
return getConfig().put(PKCE_METHOD, method);
|
||||
}
|
||||
|
||||
public String getClientAssertionSigningAlg() {
|
||||
return getConfig().get("clientAssertionSigningAlg");
|
||||
}
|
||||
|
||||
public void setClientAssertionSigningAlg(String signingAlg) {
|
||||
getConfig().put("clientAssertionSigningAlg", signingAlg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(RealmModel realm) {
|
||||
SslRequired sslRequired = realm.getSslRequired();
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
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.BrokerTestTools.createIdentityProvider;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
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.IdentityProviderRepresentation;
|
||||
|
||||
public class KcOidcBrokerClientSecretJwtCustomSignAlgTest extends AbstractBrokerTest {
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcOidcBrokerConfigurationWithJWTAuthentication();
|
||||
}
|
||||
|
||||
private class KcOidcBrokerConfigurationWithJWTAuthentication extends KcOidcBrokerConfiguration {
|
||||
|
||||
String clientSecret = UUID.randomUUID().toString();
|
||||
String signAlg = Algorithm.HS384;
|
||||
|
||||
@Override
|
||||
public List<ClientRepresentation> createProviderClients() {
|
||||
List<ClientRepresentation> clientsRepList = super.createProviderClients();
|
||||
log.info("Update provider clients to accept JWT authentication");
|
||||
for (ClientRepresentation client : clientsRepList) {
|
||||
if (client.getAttributes() == null) {
|
||||
client.setAttributes(new HashMap<String, String>());
|
||||
}
|
||||
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<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(config, syncMode);
|
||||
config.put("clientAuthMethod", OIDCLoginProtocol.CLIENT_SECRET_JWT);
|
||||
config.put("clientSecret", clientSecret);
|
||||
config.put("clientAssertionSigningAlg", signAlg);
|
||||
return idp;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<ClientRepresentation> createProviderClients() {
|
||||
List<ClientRepresentation> 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<String, String>());
|
||||
}
|
||||
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<String, String> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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=デフォルト・スコープ
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -210,6 +210,18 @@
|
|||
<input class="form-control" id="clientSecret" kc-password ng-model="identityProvider.config.clientSecret" ng-required="identityProvider.config.clientAuthMethod != 'private_key_jwt'">
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'client-secret.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="clientAssertionSigningAlg"><span data-ng-show="identityProvider.config.clientAuthMethod == 'private_key_jwt' || identityProvider.config.clientAuthMethod == 'client_secret_jwt'" class="required">*</span> {{:: 'client-assertion-signing-algorithm' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" id="clientAssertionSigningAlg" ng-required="identityProvider.config.clientAuthMethod == 'private_key_jwt' || identityProvider.config.clientAuthMethod == 'client_secret_jwt'"
|
||||
ng-model="identityProvider.config.clientAssertionSigningAlg">
|
||||
<option value=""></option>
|
||||
<option ng-repeat="provider in serverInfo.listProviderIds('signature')" value="{{provider}}">
|
||||
{{provider}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'client-assertion-signing-algorithm.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="issuer">{{:: 'issuer' | translate}} </label>
|
||||
|
|
Loading…
Reference in a new issue