Revert "Revert "Add ClientRegistration from OpenID Connect Discovery""

This reverts commit 9fe0f50e3c.

The original commit was accidentally pushed prior to PR. We attempted
to revert the commit hoping the PR would open again. This did not work.
We are going to do a Polish commit instead.

Issue: gh-5355
This commit is contained in:
Rob Winch
2018-05-18 09:40:43 -05:00
parent 9fe0f50e3c
commit 0eedfc717a
4 changed files with 343 additions and 0 deletions

View File

@@ -0,0 +1,209 @@
/*
* 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<String, Object> 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<Map<String, Object>>(){});
}
@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();
}
}