From 05dfed721a62a4117d1c8e5dcfdc7b6e26e466dd Mon Sep 17 00:00:00 2001 From: mposolda Date: Wed, 28 Jul 2021 08:22:41 +0200 Subject: [PATCH] KEYCLOAK-18636 The mtls_endpoint_aliases claim is not advertized in the discovery document --- .../representations/MTLSEndpointAliases.java | 125 ++++++++++++++++++ .../OIDCConfigurationRepresentation.java | 11 ++ .../protocol/oidc/OIDCWellKnownProvider.java | 18 +++ .../oidc/OIDCWellKnownProviderTest.java | 4 + 4 files changed, 158 insertions(+) create mode 100644 core/src/main/java/org/keycloak/protocol/oidc/representations/MTLSEndpointAliases.java diff --git a/core/src/main/java/org/keycloak/protocol/oidc/representations/MTLSEndpointAliases.java b/core/src/main/java/org/keycloak/protocol/oidc/representations/MTLSEndpointAliases.java new file mode 100644 index 0000000000..3614d354f4 --- /dev/null +++ b/core/src/main/java/org/keycloak/protocol/oidc/representations/MTLSEndpointAliases.java @@ -0,0 +1,125 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.keycloak.protocol.oidc.representations; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class MTLSEndpointAliases { + + @JsonProperty("token_endpoint") + private String tokenEndpoint; + @JsonProperty("revocation_endpoint") + private String revocationEndpoint; + @JsonProperty("introspection_endpoint") + private String introspectionEndpoint; + @JsonProperty("device_authorization_endpoint") + private String deviceAuthorizationEndpoint; + @JsonProperty("registration_endpoint") + private String registrationEndpoint; + @JsonProperty("userinfo_endpoint") + private String userInfoEndpoint; + @JsonProperty("pushed_authorization_request_endpoint") + private String pushedAuthorizationRequestEndpoint; + @JsonProperty("backchannel_authentication_endpoint") + private String backchannelAuthenticationEndpoint; + + // For custom endpoints in the future + protected Map otherClaims = new HashMap(); + + public MTLSEndpointAliases() { } + + public String getTokenEndpoint() { + return tokenEndpoint; + } + + public void setTokenEndpoint(String tokenEndpoint) { + this.tokenEndpoint = tokenEndpoint; + } + + public String getRevocationEndpoint() { + return revocationEndpoint; + } + + public void setRevocationEndpoint(String revocationEndpoint) { + this.revocationEndpoint = revocationEndpoint; + } + + public String getIntrospectionEndpoint() { + return introspectionEndpoint; + } + + public void setIntrospectionEndpoint(String introspectionEndpoint) { + this.introspectionEndpoint = introspectionEndpoint; + } + + public String getDeviceAuthorizationEndpoint() { + return deviceAuthorizationEndpoint; + } + + public void setDeviceAuthorizationEndpoint(String deviceAuthorizationEndpoint) { + this.deviceAuthorizationEndpoint = deviceAuthorizationEndpoint; + } + + public String getRegistrationEndpoint() { + return registrationEndpoint; + } + + public void setRegistrationEndpoint(String registrationEndpoint) { + this.registrationEndpoint = registrationEndpoint; + } + + public String getUserInfoEndpoint() { + return userInfoEndpoint; + } + + public void setUserInfoEndpoint(String userInfoEndpoint) { + this.userInfoEndpoint = userInfoEndpoint; + } + + public String getPushedAuthorizationRequestEndpoint() { + return pushedAuthorizationRequestEndpoint; + } + + public void setPushedAuthorizationRequestEndpoint(String pushedAuthorizationRequestEndpoint) { + this.pushedAuthorizationRequestEndpoint = pushedAuthorizationRequestEndpoint; + } + + public String getBackchannelAuthenticationEndpoint() { + return backchannelAuthenticationEndpoint; + } + + public void setBackchannelAuthenticationEndpoint(String backchannelAuthenticationEndpoint) { + this.backchannelAuthenticationEndpoint = backchannelAuthenticationEndpoint; + } + + @JsonAnyGetter + public Map getOtherClaims() { + return otherClaims; + } + + @JsonAnySetter + public void setOtherClaims(String name, Object value) { + otherClaims.put(name, value); + } +} diff --git a/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java b/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java index a469c91a69..d07706bd9b 100755 --- a/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java +++ b/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java @@ -175,6 +175,9 @@ public class OIDCConfigurationRepresentation { @JsonProperty("pushed_authorization_request_endpoint") private String pushedAuthorizationRequestEndpoint; + @JsonProperty("mtls_endpoint_aliases") + private MTLSEndpointAliases mtlsEndpointAliases; + protected Map otherClaims = new HashMap(); public String getIssuer() { @@ -525,6 +528,14 @@ public class OIDCConfigurationRepresentation { this.requirePushedAuthorizationRequests = requirePushedAuthorizationRequests; } + public MTLSEndpointAliases getMtlsEndpointAliases() { + return mtlsEndpointAliases; + } + + public void setMtlsEndpointAliases(MTLSEndpointAliases mtlsEndpointAliases) { + this.mtlsEndpointAliases = mtlsEndpointAliases; + } + @JsonAnyGetter public Map getOtherClaims() { return otherClaims; 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 7346456f91..014850ac6a 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java @@ -35,6 +35,7 @@ import org.keycloak.protocol.oidc.endpoints.TokenEndpoint; import org.keycloak.protocol.oidc.grants.ciba.CibaGrantType; import org.keycloak.protocol.oidc.grants.device.endpoints.DeviceEndpoint; import org.keycloak.protocol.oidc.par.endpoints.ParEndpoint; +import org.keycloak.protocol.oidc.representations.MTLSEndpointAliases; import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation; import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.provider.Provider; @@ -186,6 +187,9 @@ public class OIDCWellKnownProvider implements WellKnownProvider { config.setPushedAuthorizationRequestEndpoint(ParEndpoint.parUrl(backendUriInfo.getBaseUriBuilder()).build(realm.getName()).toString()); config.setRequirePushedAuthorizationRequests(Boolean.FALSE); + MTLSEndpointAliases mtlsEndpointAliases = getMtlsEndpointAliases(config); + config.setMtlsEndpointAliases(mtlsEndpointAliases); + return config; } @@ -251,4 +255,18 @@ public class OIDCWellKnownProvider implements WellKnownProvider { private List getSupportedEncryptionEnc(boolean includeNone) { return getSupportedAlgorithms(ContentEncryptionProvider.class, includeNone); } + + // Use protected method to make it easier to override in custom provider if different URLs are requested to be used as mtls_endpoint_aliases + protected MTLSEndpointAliases getMtlsEndpointAliases(OIDCConfigurationRepresentation config) { + MTLSEndpointAliases mtls_endpoints = new MTLSEndpointAliases(); + mtls_endpoints.setTokenEndpoint(config.getTokenEndpoint()); + mtls_endpoints.setRevocationEndpoint(config.getRevocationEndpoint()); + mtls_endpoints.setIntrospectionEndpoint(config.getIntrospectionEndpoint()); + mtls_endpoints.setDeviceAuthorizationEndpoint(config.getDeviceAuthorizationEndpoint()); + mtls_endpoints.setRegistrationEndpoint(config.getRegistrationEndpoint()); + mtls_endpoints.setUserInfoEndpoint(config.getUserinfoEndpoint()); + mtls_endpoints.setBackchannelAuthenticationEndpoint(config.getBackchannelAuthenticationEndpoint()); + mtls_endpoints.setPushedAuthorizationRequestEndpoint(config.getPushedAuthorizationRequestEndpoint()); + return mtls_endpoints; + } } 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 aea0066da3..052ea8394b 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 @@ -31,6 +31,7 @@ import org.keycloak.jose.jwk.JSONWebKeySet; import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.protocol.oidc.OIDCWellKnownProviderFactory; +import org.keycloak.protocol.oidc.representations.MTLSEndpointAliases; import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation; import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.representations.IDToken; @@ -175,6 +176,9 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest { // KEYCLOAK-6771 Certificate Bound Token // https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-6.2 Assert.assertTrue(oidcConfig.getTlsClientCertificateBoundAccessTokens()); + MTLSEndpointAliases mtlsEndpointAliases = oidcConfig.getMtlsEndpointAliases(); + Assert.assertEquals(oidcConfig.getTokenEndpoint(), mtlsEndpointAliases.getTokenEndpoint()); + Assert.assertEquals(oidcConfig.getRevocationEndpoint(), mtlsEndpointAliases.getRevocationEndpoint()); // CIBA assertEquals(oidcConfig.getBackchannelAuthenticationEndpoint(), oauth.getBackchannelAuthenticationUrl());