|
@@ -54,6 +54,8 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
|
import org.springframework.security.core.Authentication;
|
|
|
import org.springframework.security.core.GrantedAuthority;
|
|
|
+import org.springframework.security.core.session.SessionRegistry;
|
|
|
+import org.springframework.security.core.session.SessionRegistryImpl;
|
|
|
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
|
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
|
@@ -131,6 +133,7 @@ public class OidcTests {
|
|
|
private static JWKSource<SecurityContext> jwkSource;
|
|
|
private static HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
|
|
|
new OAuth2AccessTokenResponseHttpMessageConverter();
|
|
|
+ private static SessionRegistry sessionRegistry;
|
|
|
|
|
|
public final SpringTestContext spring = new SpringTestContext();
|
|
|
|
|
@@ -163,6 +166,7 @@ public class OidcTests {
|
|
|
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
|
|
|
.addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
|
|
|
.build();
|
|
|
+ sessionRegistry = spy(new SessionRegistryImpl());
|
|
|
}
|
|
|
|
|
|
@AfterEach
|
|
@@ -187,8 +191,8 @@ public class OidcTests {
|
|
|
|
|
|
MultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient);
|
|
|
MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
|
|
|
- .params(authorizationRequestParameters)
|
|
|
- .with(user("user").roles("A", "B")))
|
|
|
+ .params(authorizationRequestParameters)
|
|
|
+ .with(user("user").roles("A", "B")))
|
|
|
.andExpect(status().is3xxRedirection())
|
|
|
.andReturn();
|
|
|
String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
|
|
@@ -199,9 +203,9 @@ public class OidcTests {
|
|
|
OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE);
|
|
|
|
|
|
mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
|
|
|
- .params(getTokenRequestParameters(registeredClient, authorization))
|
|
|
- .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
|
|
- registeredClient.getClientId(), registeredClient.getClientSecret())))
|
|
|
+ .params(getTokenRequestParameters(registeredClient, authorization))
|
|
|
+ .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
|
|
+ registeredClient.getClientId(), registeredClient.getClientSecret())))
|
|
|
.andExpect(status().isOk())
|
|
|
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
|
|
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
|
@@ -259,8 +263,7 @@ public class OidcTests {
|
|
|
mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
|
|
|
.params(getTokenRequestParameters(registeredClient, authorization))
|
|
|
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
|
|
- registeredClient.getClientId(), registeredClient.getClientSecret()))
|
|
|
- .session(session))
|
|
|
+ registeredClient.getClientId(), registeredClient.getClientSecret())))
|
|
|
.andExpect(status().isOk())
|
|
|
.andReturn();
|
|
|
|
|
@@ -283,6 +286,85 @@ public class OidcTests {
|
|
|
assertThat(session.isInvalid()).isTrue();
|
|
|
}
|
|
|
|
|
|
+ @Test
|
|
|
+ public void requestWhenLogoutRequestWithOtherUsersIdTokenThenNotLogout() throws Exception {
|
|
|
+ this.spring.register(AuthorizationServerConfiguration.class).autowire();
|
|
|
+
|
|
|
+ // Login user1
|
|
|
+ RegisteredClient registeredClient1 = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();
|
|
|
+ this.registeredClientRepository.save(registeredClient1);
|
|
|
+
|
|
|
+ MultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient1);
|
|
|
+ MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
|
|
|
+ .params(authorizationRequestParameters)
|
|
|
+ .with(user("user1")))
|
|
|
+ .andExpect(status().is3xxRedirection())
|
|
|
+ .andReturn();
|
|
|
+
|
|
|
+ MockHttpSession user1Session = (MockHttpSession) mvcResult.getRequest().getSession();
|
|
|
+ assertThat(user1Session.isNew()).isTrue();
|
|
|
+
|
|
|
+ String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
|
|
|
+ String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code");
|
|
|
+ OAuth2Authorization user1Authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE);
|
|
|
+
|
|
|
+ mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
|
|
|
+ .params(getTokenRequestParameters(registeredClient1, user1Authorization))
|
|
|
+ .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
|
|
+ registeredClient1.getClientId(), registeredClient1.getClientSecret())))
|
|
|
+ .andExpect(status().isOk())
|
|
|
+ .andReturn();
|
|
|
+
|
|
|
+ MockHttpServletResponse servletResponse = mvcResult.getResponse();
|
|
|
+ MockClientHttpResponse httpResponse = new MockClientHttpResponse(
|
|
|
+ servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus()));
|
|
|
+ OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);
|
|
|
+
|
|
|
+ String user1IdToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN);
|
|
|
+
|
|
|
+ // Login user2
|
|
|
+ RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient2().scope(OidcScopes.OPENID).build();
|
|
|
+ this.registeredClientRepository.save(registeredClient2);
|
|
|
+
|
|
|
+ authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient2);
|
|
|
+ mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
|
|
|
+ .params(authorizationRequestParameters)
|
|
|
+ .with(user("user2")))
|
|
|
+ .andExpect(status().is3xxRedirection())
|
|
|
+ .andReturn();
|
|
|
+
|
|
|
+ MockHttpSession user2Session = (MockHttpSession) mvcResult.getRequest().getSession();
|
|
|
+ assertThat(user2Session.isNew()).isTrue();
|
|
|
+
|
|
|
+ redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
|
|
|
+ authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code");
|
|
|
+ OAuth2Authorization user2Authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE);
|
|
|
+
|
|
|
+ mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
|
|
|
+ .params(getTokenRequestParameters(registeredClient2, user2Authorization))
|
|
|
+ .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
|
|
+ registeredClient2.getClientId(), registeredClient2.getClientSecret())))
|
|
|
+ .andExpect(status().isOk())
|
|
|
+ .andReturn();
|
|
|
+
|
|
|
+ servletResponse = mvcResult.getResponse();
|
|
|
+ httpResponse = new MockClientHttpResponse(
|
|
|
+ servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus()));
|
|
|
+ accessTokenResponse = accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);
|
|
|
+
|
|
|
+ String user2IdToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN);
|
|
|
+
|
|
|
+ // Attempt to log out user1 using user2's ID Token
|
|
|
+ mvcResult = this.mvc.perform(post(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI)
|
|
|
+ .param("id_token_hint", user2IdToken)
|
|
|
+ .session(user1Session))
|
|
|
+ .andExpect(status().isBadRequest())
|
|
|
+ .andExpect(status().reason("[invalid_token] OpenID Connect 1.0 Logout Request Parameter: sub"))
|
|
|
+ .andReturn();
|
|
|
+
|
|
|
+ assertThat(user1Session.isInvalid()).isFalse();
|
|
|
+ }
|
|
|
+
|
|
|
@Test
|
|
|
public void requestWhenCustomTokenGeneratorThenUsed() throws Exception {
|
|
|
this.spring.register(AuthorizationServerConfigurationWithTokenGenerator.class).autowire();
|
|
@@ -403,6 +485,11 @@ public class OidcTests {
|
|
|
return NoOpPasswordEncoder.getInstance();
|
|
|
}
|
|
|
|
|
|
+ @Bean
|
|
|
+ SessionRegistry sessionRegistry() {
|
|
|
+ return sessionRegistry;
|
|
|
+ }
|
|
|
+
|
|
|
static class RowMapper extends JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper {
|
|
|
|
|
|
RowMapper(RegisteredClientRepository registeredClientRepository) {
|