From 9fe0f50e3ced98357bfaceee88c4539f03d11e45 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 18 May 2018 09:20:51 -0500 Subject: [PATCH] Revert "Add ClientRegistration from OpenID Connect Discovery" This reverts commit 0598d4773257d96ed323f98cbc7e78b55dfd516c. --- config/spring-security-config.gradle | 1 - .../client/OidcConfigurationProvider.java | 118 ---------- .../OidcConfigurationProviderTests.java | 209 ------------------ .../registration/ClientRegistration.java | 15 -- 4 files changed, 343 deletions(-) delete mode 100644 config/src/main/java/org/springframework/security/config/oauth2/client/OidcConfigurationProvider.java delete mode 100644 config/src/test/java/org/springframework/security/config/oauth2/client/OidcConfigurationProviderTests.java diff --git a/config/spring-security-config.gradle b/config/spring-security-config.gradle index e59f23fb09..fc56abf254 100644 --- a/config/spring-security-config.gradle +++ b/config/spring-security-config.gradle @@ -34,7 +34,6 @@ dependencies { testCompile apachedsDependencies testCompile powerMock2Dependencies testCompile spockDependencies - testCompile 'com.squareup.okhttp3:mockwebserver' testCompile 'ch.qos.logback:logback-classic' testCompile 'io.projectreactor.ipc:reactor-netty' testCompile 'javax.annotation:jsr250-api:1.0' diff --git a/config/src/main/java/org/springframework/security/config/oauth2/client/OidcConfigurationProvider.java b/config/src/main/java/org/springframework/security/config/oauth2/client/OidcConfigurationProvider.java deleted file mode 100644 index 77984669c5..0000000000 --- a/config/src/main/java/org/springframework/security/config/oauth2/client/OidcConfigurationProvider.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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.springframework.security.config.oauth2.client; - -import java.net.URI; -import java.util.Arrays; -import java.util.List; - -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.security.oauth2.core.ClientAuthenticationMethod; -import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames; -import org.springframework.web.client.RestTemplate; - -import com.nimbusds.oauth2.sdk.GrantType; -import com.nimbusds.oauth2.sdk.ParseException; -import com.nimbusds.oauth2.sdk.Scope; -import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata; - -/** - * Allows creating a {@link ClientRegistration.Builder} from an - * OpenID Provider Configuration. - * - * @author Rob Winch - * @since 5.1 - */ -public final class OidcConfigurationProvider { - - /** - * Given the Issuer creates a - * {@link ClientRegistration.Builder} by making an - * OpenID Provider - * Configuration Request and using the values in the - * OpenID - * Provider Configuration Response to initialize the {@link ClientRegistration.Builder}. - * - *

- * For example if the issuer provided is "https://example.com", then an "OpenID Provider Configuration Request" will - * be made to "https://example.com/.well-known/openid-configuration". The result is expected to be an "OpenID - * Provider Configuration Response". - *

- * - *

- * Example usage: - *

- *
-	 * ClientRegistration registration = OidcConfigurationProvider.issuer("https://example.com")
-	 *     .clientId("client-id")
-	 *     .clientSecret("client-secret")
-	 *     .build();
-	 * 
- * @param issuer the Issuer - * @return a {@link ClientRegistration.Builder} that was initialized by the OpenID Provider Configuration. - */ - public static ClientRegistration.Builder issuer(String issuer) { - RestTemplate rest = new RestTemplate(); - String openidConfiguration = rest.getForObject(issuer + "/.well-known/openid-configuration", String.class); - OIDCProviderMetadata metadata = parse(openidConfiguration); - String name = URI.create(issuer).getHost(); - List metadataAuthMethods = metadata.getTokenEndpointAuthMethods(); - // if null, the default includes client_secret_basic - if (metadataAuthMethods != null && !metadataAuthMethods.contains(com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.CLIENT_SECRET_BASIC)) { - throw new IllegalArgumentException("Only ClientAuthenticationMethod.BASIC is supported. The issuer \"" + issuer + "\" returned a configuration of " + metadataAuthMethods); - } - List grantTypes = metadata.getGrantTypes(); - // If null, the default includes authorization_code - if (grantTypes != null && !grantTypes.contains(GrantType.AUTHORIZATION_CODE)) { - throw new IllegalArgumentException("Only AuthorizationGrantType.AUTHORIZATION_CODE is supported. The issuer \"" + issuer + "\" returned a configuration of " + grantTypes); - } - List scopes = getScopes(metadata); - return ClientRegistration.withRegistrationId(name) - .userNameAttributeName(IdTokenClaimNames.SUB) - .scope(scopes) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) - .redirectUriTemplate("{baseUrl}/{action}/oauth2/code/{registrationId}") - .authorizationUri(metadata.getAuthorizationEndpointURI().toASCIIString()) - .jwkSetUri(metadata.getJWKSetURI().toASCIIString()) - .userInfoUri(metadata.getUserInfoEndpointURI().toASCIIString()) - .tokenUri(metadata.getTokenEndpointURI().toASCIIString()) - .clientName(issuer); - } - - private static List getScopes(OIDCProviderMetadata metadata) { - Scope scope = metadata.getScopes(); - if (scope == null) { - // If null, default to "openid" which must be supported - return Arrays.asList("openid"); - } else { - return scope.toStringList(); - } - } - - private static OIDCProviderMetadata parse(String body) { - try { - return OIDCProviderMetadata.parse(body); - } - catch (ParseException e) { - throw new RuntimeException(e); - } - } - - private OidcConfigurationProvider() {} -} diff --git a/config/src/test/java/org/springframework/security/config/oauth2/client/OidcConfigurationProviderTests.java b/config/src/test/java/org/springframework/security/config/oauth2/client/OidcConfigurationProviderTests.java deleted file mode 100644 index bd967c58ed..0000000000 --- a/config/src/test/java/org/springframework/security/config/oauth2/client/OidcConfigurationProviderTests.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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.springframework.security.config.oauth2.client; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.security.oauth2.core.ClientAuthenticationMethod; - -import java.util.Arrays; -import java.util.Map; - -import static org.assertj.core.api.Assertions.*; - -/** - * @author Rob Winch - * @since 5.1 - */ -public class OidcConfigurationProviderTests { - - /** - * Contains all optional parameters that are found in ClientRegistration - */ - private static final String DEFAULT_RESPONSE = - "{\n" - + " \"authorization_endpoint\": \"https://example.com/o/oauth2/v2/auth\", \n" - + " \"claims_supported\": [\n" - + " \"aud\", \n" - + " \"email\", \n" - + " \"email_verified\", \n" - + " \"exp\", \n" - + " \"family_name\", \n" - + " \"given_name\", \n" - + " \"iat\", \n" - + " \"iss\", \n" - + " \"locale\", \n" - + " \"name\", \n" - + " \"picture\", \n" - + " \"sub\"\n" - + " ], \n" - + " \"code_challenge_methods_supported\": [\n" - + " \"plain\", \n" - + " \"S256\"\n" - + " ], \n" - + " \"id_token_signing_alg_values_supported\": [\n" - + " \"RS256\"\n" - + " ], \n" - + " \"issuer\": \"https://example.com\", \n" - + " \"jwks_uri\": \"https://example.com/oauth2/v3/certs\", \n" - + " \"response_types_supported\": [\n" - + " \"code\", \n" - + " \"token\", \n" - + " \"id_token\", \n" - + " \"code token\", \n" - + " \"code id_token\", \n" - + " \"token id_token\", \n" - + " \"code token id_token\", \n" - + " \"none\"\n" - + " ], \n" - + " \"revocation_endpoint\": \"https://example.com/o/oauth2/revoke\", \n" - + " \"scopes_supported\": [\n" - + " \"openid\", \n" - + " \"email\", \n" - + " \"profile\"\n" - + " ], \n" - + " \"subject_types_supported\": [\n" - + " \"public\"\n" - + " ], \n" - + " \"grant_types_supported\" : [\"authorization_code\"], \n" - + " \"token_endpoint\": \"https://example.com/oauth2/v4/token\", \n" - + " \"token_endpoint_auth_methods_supported\": [\n" - + " \"client_secret_post\", \n" - + " \"client_secret_basic\"\n" - + " ], \n" - + " \"userinfo_endpoint\": \"https://example.com/oauth2/v3/userinfo\"\n" - + "}"; - - private MockWebServer server; - - private ObjectMapper mapper = new ObjectMapper(); - - private Map response; - - private String issuer; - - @Before - public void setup() throws Exception { - this.server = new MockWebServer(); - this.server.start(); - this.response = this.mapper.readValue(DEFAULT_RESPONSE, new TypeReference>(){}); - } - - @After - public void cleanup() throws Exception { - this.server.shutdown(); - } - - @Test - public void issuerWhenAllInformationThenSuccess() throws Exception { - ClientRegistration registration = registration(""); - ClientRegistration.ProviderDetails provider = registration.getProviderDetails(); - - assertThat(registration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); - assertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(registration.getRegistrationId()).isEqualTo(this.server.getHostName()); - assertThat(registration.getClientName()).isEqualTo(this.issuer); - assertThat(registration.getScopes()).containsOnly("openid", "email", "profile"); - assertThat(provider.getAuthorizationUri()).isEqualTo("https://example.com/o/oauth2/v2/auth"); - assertThat(provider.getTokenUri()).isEqualTo("https://example.com/oauth2/v4/token"); - assertThat(provider.getJwkSetUri()).isEqualTo("https://example.com/oauth2/v3/certs"); - assertThat(provider.getUserInfoEndpoint().getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo"); - } - - /** - * https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata - * - * RECOMMENDED. JSON array containing a list of the OAuth 2.0 [RFC6749] scope values that this server supports. The - * server MUST support the openid scope value. - * @throws Exception - */ - @Test - public void issuerWhenScopesNullThenScopesDefaulted() throws Exception { - this.response.remove("scopes_supported"); - - ClientRegistration registration = registration(""); - - assertThat(registration.getScopes()).containsOnly("openid"); - } - - @Test - public void issuerWhenGrantTypesSupportedNullThenDefaulted() throws Exception { - this.response.remove("grant_types_supported"); - - ClientRegistration registration = registration(""); - - assertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - } - - /** - * We currently only support authorization_code, so verify we have a meaningful error until we add support. - * @throws Exception - */ - @Test - public void issuerWhenGrantTypesSupportedInvalidThenException() throws Exception { - this.response.put("grant_types_supported", Arrays.asList("implicit")); - - assertThatThrownBy(() -> registration("")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Only AuthorizationGrantType.AUTHORIZATION_CODE is supported. The issuer \"" + this.issuer + "\" returned a configuration of [implicit]"); - } - - @Test - public void issuerWhenTokenEndpointAuthMethodsNullThenDefaulted() throws Exception { - this.response.remove("token_endpoint_auth_methods_supported"); - - ClientRegistration registration = registration(""); - - assertThat(registration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); - } - - /** - * We currently only support client_secret_basic, so verify we have a meaningful error until we add support. - * @throws Exception - */ - @Test - public void issuerWhenTokenEndpointAuthMethodsInvalidThenException() throws Exception { - this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("client_secret_post")); - - assertThatThrownBy(() -> registration("")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Only ClientAuthenticationMethod.BASIC is supported. The issuer \"" + this.issuer + "\" returned a configuration of [client_secret_post]"); - } - - private ClientRegistration registration(String path) throws Exception { - String body = this.mapper.writeValueAsString(this.response); - MockResponse mockResponse = new MockResponse() - .setBody(body) - .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); - this.server.enqueue(mockResponse); - this.issuer = this.server.url(path).toString(); - - return OidcConfigurationProvider.issuer(this.issuer) - .clientId("client-id") - .clientSecret("client-secret") - .build(); - } -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java index 080dde9d69..54e730717e 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java @@ -21,7 +21,6 @@ import org.springframework.security.oauth2.core.oidc.OidcScopes; import org.springframework.util.Assert; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; @@ -325,20 +324,6 @@ public final class ClientRegistration { return this; } - /** - * Sets the scope(s) used for the client. - * - * @param scope the scope(s) used for the client - * @return the {@link Builder} - */ - public Builder scope(Collection scope) { - if (scope != null && !scope.isEmpty()) { - this.scopes = Collections.unmodifiableSet( - new LinkedHashSet<>(scope)); - } - return this; - } - /** * Sets the uri for the authorization endpoint. *