|
|
|
@ -91,6 +91,7 @@ import org.springframework.web.bind.annotation.RequestParam; |
|
|
|
import org.springframework.web.bind.annotation.RestController; |
|
|
|
import org.springframework.web.bind.annotation.RestController; |
|
|
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc; |
|
|
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
|
|
|
import static org.hamcrest.Matchers.containsString; |
|
|
|
import static org.hamcrest.Matchers.containsString; |
|
|
|
import static org.mockito.ArgumentMatchers.any; |
|
|
|
import static org.mockito.ArgumentMatchers.any; |
|
|
|
import static org.mockito.BDDMockito.willThrow; |
|
|
|
import static org.mockito.BDDMockito.willThrow; |
|
|
|
@ -259,6 +260,22 @@ public class OidcLogoutConfigurerTests { |
|
|
|
verify(sessionRegistry).removeSessionInformation(any(OidcLogoutToken.class)); |
|
|
|
verify(sessionRegistry).removeSessionInformation(any(OidcLogoutToken.class)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
void logoutWhenProviderIssuerMissingThenThrowIllegalArgumentException() throws Exception { |
|
|
|
|
|
|
|
this.spring.register(WebServerConfig.class, OidcProviderConfig.class, ProviderIssuerMissingConfig.class) |
|
|
|
|
|
|
|
.autowire(); |
|
|
|
|
|
|
|
String registrationId = this.clientRegistration.getRegistrationId(); |
|
|
|
|
|
|
|
MockHttpSession session = login(); |
|
|
|
|
|
|
|
String logoutToken = this.mvc.perform(get("/token/logout").session(session)) |
|
|
|
|
|
|
|
.andExpect(status().isOk()) |
|
|
|
|
|
|
|
.andReturn() |
|
|
|
|
|
|
|
.getResponse() |
|
|
|
|
|
|
|
.getContentAsString(); |
|
|
|
|
|
|
|
assertThatIllegalArgumentException().isThrownBy( |
|
|
|
|
|
|
|
() -> this.mvc.perform(post(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) |
|
|
|
|
|
|
|
.param("logout_token", logoutToken))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private MockHttpSession login() throws Exception { |
|
|
|
private MockHttpSession login() throws Exception { |
|
|
|
MockMvcDispatcher dispatcher = (MockMvcDispatcher) this.web.getDispatcher(); |
|
|
|
MockMvcDispatcher dispatcher = (MockMvcDispatcher) this.web.getDispatcher(); |
|
|
|
this.mvc.perform(get("/token/logout")).andExpect(status().isUnauthorized()); |
|
|
|
this.mvc.perform(get("/token/logout")).andExpect(status().isUnauthorized()); |
|
|
|
@ -410,6 +427,54 @@ public class OidcLogoutConfigurerTests { |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Configuration |
|
|
|
|
|
|
|
static class ProviderIssuerMissingRegistrationConfig { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired(required = false) |
|
|
|
|
|
|
|
MockWebServer web; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
|
|
|
ClientRegistration clientRegistration() { |
|
|
|
|
|
|
|
if (this.web == null) { |
|
|
|
|
|
|
|
return TestClientRegistrations.clientRegistration().issuerUri(null).build(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
String issuer = this.web.url("/").toString(); |
|
|
|
|
|
|
|
return TestClientRegistrations.clientRegistration() |
|
|
|
|
|
|
|
.issuerUri(null) |
|
|
|
|
|
|
|
.jwkSetUri(issuer + "jwks") |
|
|
|
|
|
|
|
.tokenUri(issuer + "token") |
|
|
|
|
|
|
|
.userInfoUri(issuer + "user") |
|
|
|
|
|
|
|
.scope("openid") |
|
|
|
|
|
|
|
.build(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
|
|
|
ClientRegistrationRepository clientRegistrationRepository(ClientRegistration clientRegistration) { |
|
|
|
|
|
|
|
return new InMemoryClientRegistrationRepository(clientRegistration); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Configuration |
|
|
|
|
|
|
|
@EnableWebSecurity |
|
|
|
|
|
|
|
@Import(ProviderIssuerMissingRegistrationConfig.class) |
|
|
|
|
|
|
|
static class ProviderIssuerMissingConfig { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
|
|
|
@Order(1) |
|
|
|
|
|
|
|
SecurityFilterChain filters(HttpSecurity http) throws Exception { |
|
|
|
|
|
|
|
// @formatter:off
|
|
|
|
|
|
|
|
http |
|
|
|
|
|
|
|
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()) |
|
|
|
|
|
|
|
.oauth2Login(Customizer.withDefaults()) |
|
|
|
|
|
|
|
.oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults())); |
|
|
|
|
|
|
|
// @formatter:on
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return http.build(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Configuration |
|
|
|
@Configuration |
|
|
|
@EnableWebSecurity |
|
|
|
@EnableWebSecurity |
|
|
|
@EnableWebMvc |
|
|
|
@EnableWebMvc |
|
|
|
@ -448,6 +513,9 @@ public class OidcLogoutConfigurerTests { |
|
|
|
@Autowired |
|
|
|
@Autowired |
|
|
|
ClientRegistration registration; |
|
|
|
ClientRegistration registration; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired(required = false) |
|
|
|
|
|
|
|
MockWebServer web; |
|
|
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
@Bean |
|
|
|
@Order(0) |
|
|
|
@Order(0) |
|
|
|
SecurityFilterChain authorizationServer(HttpSecurity http, ClientRegistration registration) throws Exception { |
|
|
|
SecurityFilterChain authorizationServer(HttpSecurity http, ClientRegistration registration) throws Exception { |
|
|
|
@ -484,7 +552,7 @@ public class OidcLogoutConfigurerTests { |
|
|
|
HttpSession session = request.getSession(); |
|
|
|
HttpSession session = request.getSession(); |
|
|
|
JwtEncoderParameters parameters = JwtEncoderParameters |
|
|
|
JwtEncoderParameters parameters = JwtEncoderParameters |
|
|
|
.from(JwtClaimsSet.builder().id("id").subject(this.username) |
|
|
|
.from(JwtClaimsSet.builder().id("id").subject(this.username) |
|
|
|
.issuer(this.registration.getProviderDetails().getIssuerUri()).issuedAt(Instant.now()) |
|
|
|
.issuer(getIssuerUri()).issuedAt(Instant.now()) |
|
|
|
.expiresAt(Instant.now().plusSeconds(86400)).claim("scope", "openid").build()); |
|
|
|
.expiresAt(Instant.now().plusSeconds(86400)).claim("scope", "openid").build()); |
|
|
|
String token = this.encoder.encode(parameters).getTokenValue(); |
|
|
|
String token = this.encoder.encode(parameters).getTokenValue(); |
|
|
|
return new OIDCTokens(idToken(session.getId()), new BearerAccessToken(token, 86400, new Scope("openid")), null) |
|
|
|
return new OIDCTokens(idToken(session.getId()), new BearerAccessToken(token, 86400, new Scope("openid")), null) |
|
|
|
@ -492,7 +560,7 @@ public class OidcLogoutConfigurerTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
String idToken(String sessionId) { |
|
|
|
String idToken(String sessionId) { |
|
|
|
OidcIdToken token = TestOidcIdTokens.idToken().issuer(this.registration.getProviderDetails().getIssuerUri()) |
|
|
|
OidcIdToken token = TestOidcIdTokens.idToken().issuer(getIssuerUri()) |
|
|
|
.subject(this.username).expiresAt(Instant.now().plusSeconds(86400)) |
|
|
|
.subject(this.username).expiresAt(Instant.now().plusSeconds(86400)) |
|
|
|
.audience(List.of(this.registration.getClientId())).nonce(this.nonce) |
|
|
|
.audience(List.of(this.registration.getClientId())).nonce(this.nonce) |
|
|
|
.claim(LogoutTokenClaimNames.SID, sessionId).build(); |
|
|
|
.claim(LogoutTokenClaimNames.SID, sessionId).build(); |
|
|
|
@ -501,6 +569,13 @@ public class OidcLogoutConfigurerTests { |
|
|
|
return this.encoder.encode(parameters).getTokenValue(); |
|
|
|
return this.encoder.encode(parameters).getTokenValue(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private String getIssuerUri() { |
|
|
|
|
|
|
|
if (this.web == null) { |
|
|
|
|
|
|
|
return TestClientRegistrations.clientRegistration().build().getProviderDetails().getIssuerUri(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return this.web.url("/").toString(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@GetMapping("/user") |
|
|
|
@GetMapping("/user") |
|
|
|
Map<String, Object> userinfo() { |
|
|
|
Map<String, Object> userinfo() { |
|
|
|
return Map.of("sub", this.username, "id", this.username); |
|
|
|
return Map.of("sub", this.username, "id", this.username); |
|
|
|
|