KEYCLOAK-7997 Implement Client Registration Metadata based on Mutual TLS

This commit is contained in:
Takashi Norimatsu 2020-02-25 10:02:40 +09:00 committed by Marek Posolda
parent 12d965abf3
commit be0ba79daa
4 changed files with 69 additions and 0 deletions

View file

@ -100,6 +100,8 @@ public class OIDCClientRepresentation {
// https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-6.5 // https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-6.5
private Boolean tls_client_certificate_bound_access_tokens; private Boolean tls_client_certificate_bound_access_tokens;
private String tls_client_auth_subject_dn;
// OIDC Session Management // OIDC Session Management
private List<String> post_logout_redirect_uris; private List<String> post_logout_redirect_uris;
@ -446,4 +448,13 @@ public class OIDCClientRepresentation {
public void setTlsClientCertificateBoundAccessTokens(Boolean tls_client_certificate_bound_access_tokens) { public void setTlsClientCertificateBoundAccessTokens(Boolean tls_client_certificate_bound_access_tokens) {
this.tls_client_certificate_bound_access_tokens = tls_client_certificate_bound_access_tokens; this.tls_client_certificate_bound_access_tokens = tls_client_certificate_bound_access_tokens;
} }
public String getTlsClientAuthSubjectDn() {
return tls_client_auth_subject_dn;
}
public void setTlsClientAuthSubjectDn(String tls_client_auth_subject_dn) {
this.tls_client_auth_subject_dn = tls_client_auth_subject_dn;
}
} }

View file

@ -17,6 +17,7 @@
package org.keycloak.protocol.oidc; package org.keycloak.protocol.oidc;
import org.keycloak.authentication.authenticators.client.X509ClientAuthenticator;
import org.keycloak.jose.jws.Algorithm; import org.keycloak.jose.jws.Algorithm;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
@ -118,6 +119,14 @@ public class OIDCAdvancedConfigWrapper {
setAttribute(OIDCConfigAttributes.USE_MTLS_HOK_TOKEN, val); setAttribute(OIDCConfigAttributes.USE_MTLS_HOK_TOKEN, val);
} }
public String getTlsClientAuthSubjectDn() {
return getAttribute(X509ClientAuthenticator.ATTR_SUBJECT_DN);
}
public void setTlsClientAuthSubjectDn(String tls_client_auth_subject_dn) {
setAttribute(X509ClientAuthenticator.ATTR_SUBJECT_DN, tls_client_auth_subject_dn);
}
public String getPkceCodeChallengeMethod() { public String getPkceCodeChallengeMethod() {
return getAttribute(OIDCConfigAttributes.PKCE_CODE_CHALLENGE_METHOD); return getAttribute(OIDCConfigAttributes.PKCE_CODE_CHALLENGE_METHOD);
} }

View file

@ -121,6 +121,10 @@ public class DescriptionConverter {
else configWrapper.setUseMtlsHoKToken(false); else configWrapper.setUseMtlsHoKToken(false);
} }
if (clientOIDC.getTlsClientAuthSubjectDn() != null) {
configWrapper.setTlsClientAuthSubjectDn(clientOIDC.getTlsClientAuthSubjectDn());
}
if (clientOIDC.getIdTokenSignedResponseAlg() != null) { if (clientOIDC.getIdTokenSignedResponseAlg() != null) {
configWrapper.setIdTokenSignedResponseAlg(clientOIDC.getIdTokenSignedResponseAlg()); configWrapper.setIdTokenSignedResponseAlg(clientOIDC.getIdTokenSignedResponseAlg());
} }
@ -215,6 +219,9 @@ public class DescriptionConverter {
} else { } else {
response.setTlsClientCertificateBoundAccessTokens(Boolean.FALSE); response.setTlsClientCertificateBoundAccessTokens(Boolean.FALSE);
} }
if (config.getTlsClientAuthSubjectDn() != null) {
response.setTlsClientAuthSubjectDn(config.getTlsClientAuthSubjectDn());
}
if (config.getIdTokenSignedResponseAlg() != null) { if (config.getIdTokenSignedResponseAlg() != null) {
response.setIdTokenSignedResponseAlg(config.getIdTokenSignedResponseAlg()); response.setIdTokenSignedResponseAlg(config.getIdTokenSignedResponseAlg());
} }

View file

@ -22,6 +22,7 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.ClientsResource; import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.authentication.authenticators.client.X509ClientAuthenticator;
import org.keycloak.client.registration.Auth; import org.keycloak.client.registration.Auth;
import org.keycloak.client.registration.ClientRegistrationException; import org.keycloak.client.registration.ClientRegistrationException;
import org.keycloak.client.registration.HttpErrorException; import org.keycloak.client.registration.HttpErrorException;
@ -422,6 +423,47 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
clientsResource.get(samlClient.getId()).update(samlClient); clientsResource.get(samlClient.getId()).update(samlClient);
} }
@Test
public void testTlsClientAuthSubjectDn() throws Exception {
OIDCClientRepresentation response = null;
OIDCClientRepresentation updated = null;
try {
// create (no specification)
OIDCClientRepresentation clientRep = createRep();
clientRep.setTokenEndpointAuthMethod(OIDCLoginProtocol.TLS_CLIENT_AUTH);
clientRep.setTlsClientAuthSubjectDn("Ein");
response = reg.oidc().create(clientRep);
Assert.assertEquals(OIDCLoginProtocol.TLS_CLIENT_AUTH, response.getTokenEndpointAuthMethod());
Assert.assertEquals("Ein", response.getTlsClientAuthSubjectDn());
// Test Keycloak representation
ClientRepresentation kcClient = getClient(response.getClientId());
OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientRepresentation(kcClient);
Assert.assertEquals(X509ClientAuthenticator.PROVIDER_ID, kcClient.getClientAuthenticatorType());
Assert.assertEquals("Ein", config.getTlsClientAuthSubjectDn());
// update
reg.auth(Auth.token(response));
response.setTlsClientAuthSubjectDn("(.*?)(?:$)");
updated = reg.oidc().update(response);
Assert.assertEquals(OIDCLoginProtocol.TLS_CLIENT_AUTH, updated.getTokenEndpointAuthMethod());
Assert.assertEquals("(.*?)(?:$)", updated.getTlsClientAuthSubjectDn());
// Test Keycloak representation
kcClient = getClient(updated.getClientId());
config = OIDCAdvancedConfigWrapper.fromClientRepresentation(kcClient);
Assert.assertEquals(X509ClientAuthenticator.PROVIDER_ID, kcClient.getClientAuthenticatorType());
Assert.assertEquals("(.*?)(?:$)", config.getTlsClientAuthSubjectDn());
} finally {
// revert
reg.auth(Auth.token(updated));
updated.setTokenEndpointAuthMethod(null);
updated.setTlsClientAuthSubjectDn(null);
reg.oidc().update(updated);
}
}
private ClientRepresentation getKeycloakClient(String clientId) { private ClientRepresentation getKeycloakClient(String clientId) {
return ApiUtil.findClientByClientId(adminClient.realms().realm(REALM_NAME), clientId).toRepresentation(); return ApiUtil.findClientByClientId(adminClient.realms().realm(REALM_NAME), clientId).toRepresentation();
} }