diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java index e13bddf7073..bb3a3dbfb86 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -389,14 +389,14 @@ public void saml2LogoutRequestWhenNoRegistrationThen400() throws Exception { } @Test - public void saml2LogoutRequestWhenInvalidSamlRequestThen401() throws Exception { + public void saml2LogoutRequestWhenInvalidSamlRequestThen302Redirect() throws Exception { this.spring.register(Saml2LogoutDefaultsConfig.class).autowire(); this.mvc .perform(get("/logout/saml2/slo").param("SAMLRequest", this.apLogoutRequest) .param("RelayState", this.apLogoutRequestRelayState) .param("SigAlg", this.apLogoutRequestSigAlg) .with(authentication(this.user))) - .andExpect(status().isUnauthorized()); + .andExpect(status().isFound()); verifyNoInteractions(getBean(LogoutHandler.class)); } diff --git a/config/src/test/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests.java index d51349440a5..5998c313146 100644 --- a/config/src/test/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -288,14 +288,14 @@ public void saml2LogoutRequestWhenNoRegistrationThen400() throws Exception { } @Test - public void saml2LogoutRequestWhenInvalidSamlRequestThen401() throws Exception { + public void saml2LogoutRequestWhenInvalidSamlRequestThen302Redirect() throws Exception { this.spring.configLocations(this.xml("Default")).autowire(); this.mvc .perform(get("/logout/saml2/slo").param("SAMLRequest", this.apLogoutRequest) .param("RelayState", this.apLogoutRequestRelayState) .param("SigAlg", this.apLogoutRequestSigAlg) .with(authentication(this.saml2User))) - .andExpect(status().isUnauthorized()); + .andExpect(status().isFound()); } @Test diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutResponseResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutResponseResolver.java index 41a44a1856f..4033e4d4536 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutResponseResolver.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutResponseResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,8 +43,11 @@ import org.springframework.security.core.Authentication; import org.springframework.security.saml2.core.OpenSamlInitializationService; +import org.springframework.security.saml2.core.Saml2Error; +import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2ParameterNames; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; @@ -130,6 +133,16 @@ final class BaseOpenSamlLogoutResponseResolver implements Saml2LogoutResponseRes */ @Override public Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication) { + return resolve(request, authentication, StatusCode.SUCCESS); + } + + @Override + public Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication, + Saml2AuthenticationException authenticationException) { + return resolve(request, authentication, getSamlStatus(authenticationException)); + } + + private Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication, String statusCode) { LogoutRequest logoutRequest = this.saml.deserialize(extractSamlRequest(request)); String registrationId = getRegistrationId(authentication); RelyingPartyRegistration registration = this.relyingPartyRegistrationResolver.resolve(request, registrationId); @@ -152,7 +165,7 @@ public Saml2LogoutResponse resolve(HttpServletRequest request, Authentication au issuer.setValue(entityId); logoutResponse.setIssuer(issuer); StatusCode code = this.statusCodeBuilder.buildObject(); - code.setValue(StatusCode.SUCCESS); + code.setValue(statusCode); Status status = this.statusBuilder.buildObject(); status.setStatusCode(code); logoutResponse.setStatus(status); @@ -224,6 +237,15 @@ private String serialize(LogoutResponse logoutResponse) { return this.saml.serialize(logoutResponse).serialize(); } + private String getSamlStatus(Saml2AuthenticationException exception) { + Saml2Error saml2Error = exception.getSaml2Error(); + return switch (saml2Error.getErrorCode()) { + case Saml2ErrorCodes.INVALID_DESTINATION -> StatusCode.REQUEST_DENIED; + case Saml2ErrorCodes.INVALID_REQUEST -> StatusCode.REQUESTER; + default -> StatusCode.RESPONDER; + }; + } + static final class LogoutResponseParameters { private final HttpServletRequest request; diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java index 1476f37624b..a3a22568df2 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java @@ -31,6 +31,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.saml2.core.Saml2Error; +import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2ParameterNames; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; @@ -125,40 +126,31 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse chain.doFilter(request, response); return; } - RelyingPartyRegistration registration = parameters.getRelyingPartyRegistration(); - if (registration.getSingleLogoutServiceLocation() == null) { - this.logger.trace( - "Did not process logout request since RelyingPartyRegistration has not been configured with a logout request endpoint"); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - return; - } - Saml2MessageBinding saml2MessageBinding = Saml2MessageBindingUtils.resolveBinding(request); - if (!registration.getSingleLogoutServiceBindings().contains(saml2MessageBinding)) { - this.logger.trace("Did not process logout request since used incorrect binding"); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - return; + try { + validateLogoutRequest(request, parameters); } + catch (Saml2AuthenticationException ex) { + Saml2LogoutResponse errorLogoutResponse = this.logoutResponseResolver.resolve(request, authentication, ex); + if (errorLogoutResponse == null) { + this.logger.trace(LogMessage.format( + "Returning error since no error logout response could be generated: %s", ex.getSaml2Error())); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return; + } - Saml2LogoutValidatorResult result = this.logoutRequestValidator.validate(parameters); - if (result.hasErrors()) { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, result.getErrors().iterator().next().toString()); - this.logger.debug(LogMessage.format("Failed to validate LogoutRequest: %s", result.getErrors())); + sendLogoutResponse(request, response, errorLogoutResponse); return; } + this.handler.logout(request, response, authentication); Saml2LogoutResponse logoutResponse = this.logoutResponseResolver.resolve(request, authentication); if (logoutResponse == null) { - this.logger.trace("Returning 401 since no logout response generated"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + this.logger.trace("Returning error since no logout response generated"); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } - if (logoutResponse.getBinding() == Saml2MessageBinding.REDIRECT) { - doRedirect(request, response, logoutResponse); - } - else { - doPost(response, logoutResponse); - } + sendLogoutResponse(request, response, logoutResponse); } public void setLogoutRequestMatcher(RequestMatcher logoutRequestMatcher) { @@ -180,6 +172,40 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy secur this.securityContextHolderStrategy = securityContextHolderStrategy; } + private void validateLogoutRequest(HttpServletRequest request, Saml2LogoutRequestValidatorParameters parameters) { + RelyingPartyRegistration registration = parameters.getRelyingPartyRegistration(); + if (registration.getSingleLogoutServiceLocation() == null) { + this.logger.trace( + "Did not process logout request since RelyingPartyRegistration has not been configured with a logout request endpoint"); + throw new Saml2AuthenticationException(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, + "RelyingPartyRegistration has not been configured with a logout request endpoint")); + } + + Saml2MessageBinding saml2MessageBinding = Saml2MessageBindingUtils.resolveBinding(request); + if (!registration.getSingleLogoutServiceBindings().contains(saml2MessageBinding)) { + this.logger.trace("Did not process logout request since used incorrect binding"); + throw new Saml2AuthenticationException( + new Saml2Error(Saml2ErrorCodes.INVALID_REQUEST, "Logout request used invalid binding")); + } + + Saml2LogoutValidatorResult result = this.logoutRequestValidator.validate(parameters); + if (result.hasErrors()) { + this.logger.debug(LogMessage.format("Failed to validate LogoutRequest: %s", result.getErrors())); + throw new Saml2AuthenticationException( + new Saml2Error(Saml2ErrorCodes.INVALID_REQUEST, "Failed to validate the logout request")); + } + } + + private void sendLogoutResponse(HttpServletRequest request, HttpServletResponse response, + Saml2LogoutResponse logoutResponse) throws IOException { + if (logoutResponse.getBinding() == Saml2MessageBinding.REDIRECT) { + doRedirect(request, response, logoutResponse); + } + else { + doPost(response, logoutResponse); + } + } + private void doRedirect(HttpServletRequest request, HttpServletResponse response, Saml2LogoutResponse logoutResponse) throws IOException { String location = logoutResponse.getResponseLocation(); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.java index f722fd55b77..bebd44579a0 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.core.Authentication; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; @@ -44,4 +45,17 @@ public interface Saml2LogoutResponseResolver { */ Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication); + /** + * Prepare to create, sign, and serialize a SAML 2.0 Error Logout Response. + * @param request the HTTP request + * @param authentication the current user + * @param authenticationException the thrown exception when the logout request was + * processed + * @return a signed and serialized SAML 2.0 Logout Response + */ + default Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication, + Saml2AuthenticationException authenticationException) { + return null; + } + } diff --git a/saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolver.java b/saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolver.java index 4d70062b3f0..fe5a461e6da 100644 --- a/saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolver.java +++ b/saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import org.opensaml.saml.saml2.core.LogoutRequest; import org.springframework.security.core.Authentication; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; @@ -66,6 +67,15 @@ public Saml2LogoutResponse resolve(HttpServletRequest request, Authentication au return this.delegate.resolve(request, authentication); } + /** + * {@inheritDoc} + */ + @Override + public Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication, + Saml2AuthenticationException exception) { + return this.delegate.resolve(request, authentication, exception); + } + /** * Set a {@link Consumer} for modifying the OpenSAML {@link LogoutRequest} * @param parametersConsumer a consumer that accepts an diff --git a/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java b/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java index eba07f55c57..b363a06c6a8 100644 --- a/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java +++ b/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,17 +17,26 @@ package org.springframework.security.saml2.provider.service.web.authentication.logout; import java.util.function.Consumer; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.opensaml.saml.saml2.core.LogoutRequest; +import org.opensaml.saml.saml2.core.StatusCode; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.saml2.core.Saml2Error; +import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2ParameterNames; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations; import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutResponseResolver.LogoutResponseParameters; @@ -56,7 +65,7 @@ public void resolveWhenCustomParametersConsumerThenUses() { logoutResponseResolver.setParametersConsumer(parametersConsumer); MockHttpServletRequest request = new MockHttpServletRequest(); RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration() - .assertingPartyDetails( + .assertingPartyMetadata( (party) -> party.singleLogoutServiceResponseLocation("https://ap.example.com/logout")) .build(); Authentication authentication = new TestingAuthenticationToken("user", "password"); @@ -69,6 +78,27 @@ public void resolveWhenCustomParametersConsumerThenUses() { verify(parametersConsumer).accept(any()); } + @ParameterizedTest + @MethodSource("provideAuthExceptionAndExpectedSamlStatusCode") + public void resolveWithAuthException(Saml2AuthenticationException exception, String expectedStatusCode) { + OpenSaml4LogoutResponseResolver logoutResponseResolver = new OpenSaml4LogoutResponseResolver( + this.relyingPartyRegistrationResolver); + MockHttpServletRequest request = new MockHttpServletRequest(); + RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration() + .assertingPartyMetadata( + (party) -> party.singleLogoutServiceResponseLocation("https://ap.example.com/logout") + .singleLogoutServiceBinding(Saml2MessageBinding.POST)) + .build(); + Authentication authentication = new TestingAuthenticationToken("user", "password"); + LogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration); + request.setParameter(Saml2ParameterNames.SAML_REQUEST, + Saml2Utils.samlEncode(this.saml.serialize(logoutRequest).serialize().getBytes())); + given(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration); + Saml2LogoutResponse logoutResponse = logoutResponseResolver.resolve(request, authentication, exception); + assertThat(logoutResponse).isNotNull(); + assertThat(new String(Saml2Utils.samlDecode(logoutResponse.getSamlResponse()))).contains(expectedStatusCode); + } + @Test public void setParametersConsumerWhenNullThenIllegalArgument() { OpenSaml4LogoutRequestResolver logoutRequestResolver = new OpenSaml4LogoutRequestResolver( @@ -77,4 +107,14 @@ public void setParametersConsumerWhenNullThenIllegalArgument() { .isThrownBy(() -> logoutRequestResolver.setParametersConsumer(null)); } + private static Stream provideAuthExceptionAndExpectedSamlStatusCode() { + return Stream.of( + Arguments.of(new Saml2AuthenticationException(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, "")), + StatusCode.REQUEST_DENIED), + Arguments.of(new Saml2AuthenticationException(new Saml2Error(Saml2ErrorCodes.INVALID_REQUEST, "")), + StatusCode.REQUESTER) + + ); + } + } diff --git a/saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5LogoutResponseResolver.java b/saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5LogoutResponseResolver.java index 4d0b9175978..2a04353e97a 100644 --- a/saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5LogoutResponseResolver.java +++ b/saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5LogoutResponseResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import org.opensaml.saml.saml2.core.LogoutRequest; import org.springframework.security.core.Authentication; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; @@ -66,6 +67,15 @@ public Saml2LogoutResponse resolve(HttpServletRequest request, Authentication au return this.delegate.resolve(request, authentication); } + /** + * {@inheritDoc} + */ + @Override + public Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication, + Saml2AuthenticationException exception) { + return this.delegate.resolve(request, authentication, exception); + } + /** * Set a {@link Consumer} for modifying the OpenSAML {@link LogoutRequest} * @param parametersConsumer a consumer that accepts an diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilterTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilterTests.java index 6b6fcea452d..f03c143ca08 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilterTests.java +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.saml2.core.Saml2Error; +import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2ParameterNames; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse; @@ -40,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.mock; import static org.mockito.Mockito.verify; @@ -97,7 +99,7 @@ public void doFilterWhenSamlRequestThenRedirects() throws Exception { @Test public void doFilterWhenSamlRequestThenPosts() throws Exception { RelyingPartyRegistration registration = TestRelyingPartyRegistrations.full() - .assertingPartyDetails((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST)) + .assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST)) .build(); Authentication authentication = new TestingAuthenticationToken("user", "password"); given(this.securityContextHolderStrategy.getContext()).willReturn(new SecurityContextImpl(authentication)); @@ -117,12 +119,7 @@ public void doFilterWhenSamlRequestThenPosts() throws Exception { verify(this.logoutRequestValidator).validate(any()); verify(this.logoutHandler).logout(any(), any(), any()); verify(this.logoutResponseResolver).resolve(any(), any()); - String content = response.getContentAsString(); - assertThat(content).contains(Saml2ParameterNames.SAML_RESPONSE); - assertThat(content).contains(registration.getAssertingPartyDetails().getSingleLogoutServiceResponseLocation()); - assertThat(content).contains( - "