|
|
|
@ -1071,6 +1071,94 @@ public class OAuth2AuthorizationCodeGrantTests { |
|
|
|
.isEqualTo(true); |
|
|
|
.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) { |
|
|
|
private static OAuth2Authorization createAuthorization(RegisteredClient registeredClient) { |
|
|
|
Map<String, Object> additionalParameters = new HashMap<>(); |
|
|
|
Map<String, Object> additionalParameters = new HashMap<>(); |
|
|
|
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE); |
|
|
|
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE); |
|
|
|
@ -1125,8 +1213,8 @@ public class OAuth2AuthorizationCodeGrantTests { |
|
|
|
private static String getAuthorizationHeader(RegisteredClient registeredClient) throws Exception { |
|
|
|
private static String getAuthorizationHeader(RegisteredClient registeredClient) throws Exception { |
|
|
|
String clientId = registeredClient.getClientId(); |
|
|
|
String clientId = registeredClient.getClientId(); |
|
|
|
String clientSecret = registeredClient.getClientSecret(); |
|
|
|
String clientSecret = registeredClient.getClientSecret(); |
|
|
|
clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name()); |
|
|
|
clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8); |
|
|
|
clientSecret = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8.name()); |
|
|
|
clientSecret = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8); |
|
|
|
String credentialsString = clientId + ":" + clientSecret; |
|
|
|
String credentialsString = clientId + ":" + clientSecret; |
|
|
|
byte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8)); |
|
|
|
byte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8)); |
|
|
|
return "Basic " + new String(encodedBytes, 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|