KEYCLOAK-7997 Implement Client Registration Metadata based on Mutual TLS
This commit is contained in:
parent
12d965abf3
commit
be0ba79daa
4 changed files with 69 additions and 0 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue