|
@@ -1071,6 +1071,94 @@ public class OAuth2AuthorizationCodeGrantTests {
|
|
|
.isEqualTo(true);
|
|
|
}
|
|
|
|
|
|
+ // gh-2182
|
|
|
+ @Test
|
|
|
+ public void requestWhenPushedAuthorizationRequestAndRequiresConsentThenDisplaysConsentPage() throws Exception {
|
|
|
+ this.spring.register(AuthorizationServerConfigurationWithPushedAuthorizationRequests.class).autowire();
|
|
|
+
|
|
|
+ RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {
|
|
|
+ scopes.clear();
|
|
|
+ scopes.add("message.read");
|
|
|
+ scopes.add("message.write");
|
|
|
+ }).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();
|
|
|
+ this.registeredClientRepository.save(registeredClient);
|
|
|
+
|
|
|
+ MvcResult mvcResult = this.mvc
|
|
|
+ .perform(post("/oauth2/par").params(getAuthorizationRequestParameters(registeredClient))
|
|
|
+ .header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))
|
|
|
+ .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
|
|
+ .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
|
|
+ .andExpect(status().isCreated())
|
|
|
+ .andExpect(jsonPath("$.request_uri").isNotEmpty())
|
|
|
+ .andExpect(jsonPath("$.expires_in").isNotEmpty())
|
|
|
+ .andReturn();
|
|
|
+
|
|
|
+ String requestUri = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.request_uri");
|
|
|
+
|
|
|
+ String consentPage = this.mvc
|
|
|
+ .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
|
|
|
+ .queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
|
|
|
+ .queryParam(OAuth2ParameterNames.REQUEST_URI, requestUri)
|
|
|
+ .with(user("user")))
|
|
|
+ .andExpect(status().is2xxSuccessful())
|
|
|
+ .andReturn()
|
|
|
+ .getResponse()
|
|
|
+ .getContentAsString();
|
|
|
+
|
|
|
+ assertThat(consentPage).contains("Consent required");
|
|
|
+ assertThat(consentPage).contains(scopeCheckbox("message.read"));
|
|
|
+ assertThat(consentPage).contains(scopeCheckbox("message.write"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // gh-2182
|
|
|
+ @Test
|
|
|
+ public void requestWhenPushedAuthorizationRequestAndCustomConsentPageConfiguredThenRedirect() throws Exception {
|
|
|
+ this.spring.register(AuthorizationServerConfigurationWithPushedAuthorizationRequestsAndCustomConsentPage.class)
|
|
|
+ .autowire();
|
|
|
+
|
|
|
+ RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {
|
|
|
+ scopes.clear();
|
|
|
+ scopes.add("message.read");
|
|
|
+ scopes.add("message.write");
|
|
|
+ }).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();
|
|
|
+ this.registeredClientRepository.save(registeredClient);
|
|
|
+
|
|
|
+ MvcResult mvcResult = this.mvc
|
|
|
+ .perform(post("/oauth2/par").params(getAuthorizationRequestParameters(registeredClient))
|
|
|
+ .header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))
|
|
|
+ .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
|
|
+ .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
|
|
+ .andExpect(status().isCreated())
|
|
|
+ .andExpect(jsonPath("$.request_uri").isNotEmpty())
|
|
|
+ .andExpect(jsonPath("$.expires_in").isNotEmpty())
|
|
|
+ .andReturn();
|
|
|
+
|
|
|
+ String requestUri = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.request_uri");
|
|
|
+
|
|
|
+ mvcResult = this.mvc
|
|
|
+ .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
|
|
|
+ .queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
|
|
|
+ .queryParam(OAuth2ParameterNames.REQUEST_URI, requestUri)
|
|
|
+ .with(user("user")))
|
|
|
+ .andExpect(status().is3xxRedirection())
|
|
|
+ .andReturn();
|
|
|
+ String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
|
|
|
+ assertThat(redirectedUrl).matches("http://localhost/oauth2/consent\\?scope=.+&client_id=.+&state=.+");
|
|
|
+
|
|
|
+ String locationHeader = URLDecoder.decode(redirectedUrl, StandardCharsets.UTF_8.name());
|
|
|
+ UriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build();
|
|
|
+ MultiValueMap<String, String> redirectQueryParams = uriComponents.getQueryParams();
|
|
|
+
|
|
|
+ assertThat(uriComponents.getPath()).isEqualTo(consentPage);
|
|
|
+ assertThat(redirectQueryParams.getFirst(OAuth2ParameterNames.SCOPE)).isEqualTo("message.read message.write");
|
|
|
+ assertThat(redirectQueryParams.getFirst(OAuth2ParameterNames.CLIENT_ID))
|
|
|
+ .isEqualTo(registeredClient.getClientId());
|
|
|
+
|
|
|
+ String state = extractParameterFromRedirectUri(redirectedUrl, "state");
|
|
|
+ OAuth2Authorization authorization = this.authorizationService.findByToken(state, STATE_TOKEN_TYPE);
|
|
|
+ assertThat(authorization).isNotNull();
|
|
|
+ }
|
|
|
+
|
|
|
private static OAuth2Authorization createAuthorization(RegisteredClient registeredClient) {
|
|
|
Map<String, Object> additionalParameters = new HashMap<>();
|
|
|
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);
|
|
@@ -1125,8 +1213,8 @@ public class OAuth2AuthorizationCodeGrantTests {
|
|
|
private static String getAuthorizationHeader(RegisteredClient registeredClient) throws Exception {
|
|
|
String clientId = registeredClient.getClientId();
|
|
|
String clientSecret = registeredClient.getClientSecret();
|
|
|
- clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());
|
|
|
- clientSecret = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8.name());
|
|
|
+ clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8);
|
|
|
+ clientSecret = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8);
|
|
|
String credentialsString = clientId + ":" + clientSecret;
|
|
|
byte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8));
|
|
|
return "Basic " + new String(encodedBytes, StandardCharsets.UTF_8);
|
|
@@ -1496,4 +1584,28 @@ public class OAuth2AuthorizationCodeGrantTests {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ @EnableWebSecurity
|
|
|
+ @Configuration(proxyBeanMethods = false)
|
|
|
+ static class AuthorizationServerConfigurationWithPushedAuthorizationRequestsAndCustomConsentPage
|
|
|
+ extends AuthorizationServerConfiguration {
|
|
|
+
|
|
|
+ // @formatter:off
|
|
|
+ @Bean
|
|
|
+ SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
|
|
+ http
|
|
|
+ .oauth2AuthorizationServer((authorizationServer) ->
|
|
|
+ authorizationServer
|
|
|
+ .pushedAuthorizationRequestEndpoint(Customizer.withDefaults())
|
|
|
+ .authorizationEndpoint((authorizationEndpoint) ->
|
|
|
+ authorizationEndpoint.consentPage(consentPage))
|
|
|
+ )
|
|
|
+ .authorizeHttpRequests((authorize) ->
|
|
|
+ authorize.anyRequest().authenticated()
|
|
|
+ );
|
|
|
+ return http.build();
|
|
|
+ }
|
|
|
+ // @formatter:on
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
}
|