Support for OIDC RP-Initiated Logout

Fixes: gh-5350
This commit is contained in:
Josh Cummings
2018-05-16 14:12:28 -06:00
parent 55e8df1efe
commit 248a8c030b
5 changed files with 367 additions and 10 deletions

View File

@@ -15,10 +15,20 @@
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpHeaders;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
@@ -35,17 +45,22 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.web.oidc.logout.OidcClientInitiatedLogoutSuccessHandler;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
@@ -61,6 +76,7 @@ import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
import org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
@@ -71,21 +87,18 @@ import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
/**
* Tests for {@link OAuth2LoginConfigurer}.
@@ -115,6 +128,12 @@ public class OAuth2LoginConfigurerTests {
@Autowired
SecurityContextRepository securityContextRepository;
@Rule
public final SpringTestRule spring = new SpringTestRule();
@Autowired(required = false)
MockMvc mvc;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MockFilterChain filterChain;
@@ -455,6 +474,21 @@ public class OAuth2LoginConfigurerTests {
"available: expected single matching bean but found 2: jwtDecoderFactory1,jwtDecoderFactory2");
}
@Test
public void logoutWhenUsingOidcLogoutHandlerThenRedirects() throws Exception {
this.spring.register(OAuth2LoginConfigWithOidcLogoutSuccessHandler.class).autowire();
OAuth2AuthenticationToken token = new OAuth2AuthenticationToken(
TestOidcUsers.create(),
AuthorityUtils.NO_AUTHORITIES,
"registration-id");
this.mvc.perform(post("/logout")
.with(authentication(token))
.with(csrf()))
.andExpect(redirectedUrl("http://logout?id_token_hint=id-token"));
}
private void loadConfig(Class<?>... configs) {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(configs);
@@ -591,6 +625,31 @@ public class OAuth2LoginConfigurerTests {
}
}
@EnableWebSecurity
static class OAuth2LoginConfigWithOidcLogoutSuccessHandler extends CommonWebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.logout()
.logoutSuccessHandler(oidcLogoutSuccessHandler());
super.configure(http);
}
@Bean
OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler() {
return new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository());
}
@Bean
ClientRegistrationRepository clientRegistrationRepository() {
Map<String, Object> providerMetadata =
Collections.singletonMap("end_session_endpoint", "http://logout");
return new InMemoryClientRegistrationRepository(
TestClientRegistrations.clientRegistration()
.providerConfigurationMetadata(providerMetadata).build());
}
}
private static abstract class CommonWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {