123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- [[servlet-saml2login-logout]]
- = Performing Single Logout
- Among its xref:servlet/authentication/logout.adoc[other logout mechanisms], Spring Security ships with support for RP- and AP-initiated SAML 2.0 Single Logout.
- Briefly, there are two use cases Spring Security supports:
- * **RP-Initiated** - Your application has an endpoint that, when POSTed to, will logout the user and send a `saml2:LogoutRequest` to the asserting party.
- Thereafter, the asserting party will send back a `saml2:LogoutResponse` and allow your application to respond
- * **AP-Initiated** - Your application has an endpoint that will receive a `saml2:LogoutRequest` from the asserting party.
- Your application will complete its logout at that point and then send a `saml2:LogoutResponse` to the asserting party.
- [NOTE]
- In the **AP-Initiated** scenario, any local redirection that your application would do post-logout is rendered moot.
- Once your application sends a `saml2:LogoutResponse`, it no longer has control of the browser.
- == Minimal Configuration for Single Logout
- To use Spring Security's SAML 2.0 Single Logout feature, you will need the following things:
- * First, the asserting party must support SAML 2.0 Single Logout
- * Second, the asserting party should be configured to sign and POST `saml2:LogoutRequest` s and `saml2:LogoutResponse` s your application's `/logout/saml2/slo` endpoint
- * Third, your application must have a PKCS#8 private key and X.509 certificate for signing `saml2:LogoutRequest` s and `saml2:LogoutResponse` s
- You can achieve this in Spring Boot in the following way:
- [source,yaml]
- ----
- spring:
- security:
- saml2:
- relyingparty:
- registration:
- metadata:
- signing.credentials: <3>
- - private-key-location: classpath:credentials/rp-private.key
- certificate-location: classpath:credentials/rp-certificate.crt
- singlelogout.url: "{baseUrl}/logout/saml2/slo" <2>
- assertingparty:
- metadata-uri: https://ap.example.com/metadata <1>
- ----
- <1> - The metadata URI of the IDP, which will indicate to your application its support of SLO
- <2> - The SLO endpoint in your application
- <3> - The signing credentials to sign ``<saml2:LogoutRequest>``s and ``<saml2:LogoutResponse>``s
- [NOTE]
- ----
- An asserting party supports Single Logout if their metadata includes the `<SingleLogoutService>` element in their metadata.
- ----
- And that's it!
- Spring Security's logout support offers a number of configuration points.
- Consider the following use cases:
- * Understand how the above <<_startup_expectations, minimal configuration works>>
- * Get a picture of <<architecture, the overall architecture>>
- * Allow users to <<separating-local-saml2-logout, logout out of the app only>>
- * Customize <<_configuring_logout_endpoints, logout endpoints>>
- * Storing `<saml2:LogoutRequests>` somewhere <<_customizing_storage, other than the session>>
- === Startup Expectations
- When these properties are used, in addition to login, SAML 2.0 Service Provider will automatically configure itself facilitate logout by way of ``<saml2:LogoutRequest>``s and ``<saml2:LogoutResponse>``s using either RP- or AP-initiated logout.
- It achieves this through a deterministic startup process:
- 1. Query the Identity Server Metadata endpoint for the `<SingleLogoutService>` element
- 2. Scan the metadata and cache any public signature verification keys
- 3. Prepare the appropriate endpoints
- A consequence of this process is that the identity server must be up and receiving requests in order for Service Provider to successfully start up.
- [NOTE]
- If the identity server is down when Service Provider queries it (given appropriate timeouts), then startup will fail.
- === Runtime Expectations
- Given the above configuration any logged-in user can send a `POST /logout` to your application to perform RP-initiated SLO.
- Your application will then do the following:
- 1. Logout the user and invalidate the session
- 2. Produce a `<saml2:LogoutRequest>` and POST it to the associated asserting party's SLO endpoint
- 3. Then, if the asserting party responds with a `<saml2:LogoutResponse>`, the application with verify it and redirect to the configured success endpoint
- Also, your application can participate in an AP-initiated logout when the asserting party sends a `<saml2:LogoutRequest>` to `/logout/saml2/slo`.
- When this happens, your application will do the following:
- 1. Verify the `<saml2:LogoutRequest>`
- 2. Logout the user and invalidate the session
- 3. Produce a `<saml2:LogoutResponse>` and POST it back to the asserting party's SLO endpoint
- == Minimal Configuration Sans Boot
- Instead of Boot properties, you can also achieve the same outcome by publishing the beans directly like so:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- public class SecurityConfig {
- @Value("${private.key}") RSAPrivateKey key;
- @Value("${public.certificate}") X509Certificate certificate;
- @Bean
- RelyingPartyRegistrationRepository registrations() {
- Saml2X509Credential credential = Saml2X509Credential.signing(key, certificate);
- RelyingPartyRegistration registration = RelyingPartyRegistrations
- .fromMetadataLocation("https://ap.example.org/metadata") <1>
- .registrationId("metadata")
- .singleLogoutServiceLocation("{baseUrl}/logout/saml2/slo") <2>
- .signingX509Credentials((signing) -> signing.add(credential)) <3>
- .build();
- return new InMemoryRelyingPartyRegistrationRepository(registration);
- }
- @Bean
- SecurityFilterChain web(HttpSecurity http) throws Exception {
- http
- .authorizeHttpRequests((authorize) -> authorize
- .anyRequest().authenticated()
- )
- .saml2Login(withDefaults())
- .saml2Logout(withDefaults()); <4>
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @Configuration
- class SecurityConfig(@Value("${private.key}") val key: RSAPrivateKey,
- @Value("${public.certificate}") val certificate: X509Certificate) {
- @Bean
- fun registrations(): RelyingPartyRegistrationRepository {
- val credential = Saml2X509Credential.signing(key, certificate)
- val registration = RelyingPartyRegistrations
- .fromMetadataLocation("https://ap.example.org/metadata") <1>
- .registrationId("metadata")
- .singleLogoutServiceLocation("{baseUrl}/logout/saml2/slo") <2>
- .signingX509Credentials({ signing: List<Saml2X509Credential> -> signing.add(credential) }) <3>
- .build()
- return InMemoryRelyingPartyRegistrationRepository(registration)
- }
- @Bean
- fun web(http: HttpSecurity): SecurityFilterChain {
- http {
- authorizeHttpRequests {
- anyRequest = authenticated
- }
- saml2Login {
- }
- saml2Logout { <4>
- }
- }
- return http.build()
- }
- }
- ----
- ======
- <1> - The metadata URI of the IDP, which will indicate to your application its support of SLO
- <2> - The SLO endpoint in your application
- <3> - The signing credentials to sign ``<saml2:LogoutRequest>``s and ``<saml2:LogoutResponse>``s, which you can also add to xref:servlet/saml2/login/overview.adoc#servlet-saml2login-rpr-duplicated[multiple relying parties]
- <4> - Second, indicate that your application wants to use SAML SLO to logout the end user
- [NOTE]
- Adding `saml2Logout` adds the capability for logout to your service provider as a whole.
- Because it is an optional capability, you need to enable it for each individual `RelyingPartyRegistration`.
- You do this by setting the `RelyingPartyRegistration.Builder#singleLogoutServiceLocation` property as seen above.
- [[architecture]]
- == How Saml 2.0 Logout Works
- Next, let's see the architectural components that Spring Security uses to support https://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf#page=37[SAML 2.0 Logout] in servlet-based applications, like the one we just saw.
- For RP-initiated logout:
- image:{icondir}/number_1.png[] Spring Security executes its xref:servlet/authentication/logout.adoc#logout-architecture[logout flow], calling its ``LogoutHandler``s to invalidate the session and perform other cleanup.
- It then invokes the javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2RelyingPartyInitiatedLogoutSuccessHandler[].
- image:{icondir}/number_2.png[] The logout success handler uses an instance of
- javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver[] to create, sign, and serialize a `<saml2:LogoutRequest>`.
- It uses the keys and configuration from the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`] that is associated with the current `Saml2AuthenticatedPrincipal`.
- Then, it redirect-POSTs the `<saml2:LogoutRequest>` to the asserting party SLO endpoint
- The browser hands control over to the asserting party.
- If the asserting party redirects back (which it may not), then the application proceeds to step image:{icondir}/number_3.png[].
- image:{icondir}/number_3.png[] The javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter[] deserializes, verifies, and processes the `<saml2:LogoutResponse>` with its javadoc:org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator[].
- image:{icondir}/number_4.png[] If valid, then it completes the local logout flow by redirecting to `/login?logout`, or whatever has been configured.
- If invalid, then it responds with a 400.
- For AP-initiated logout:
- image:{icondir}/number_1.png[] The javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter[] deserializes, verifies, and processes the `<saml2:LogoutRequest>` with its javadoc:org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator[].
- image:{icondir}/number_2.png[] If valid, then the filter calls the configured ``LogoutHandler``s, invalidating the session and performing other cleanup.
- image:{icondir}/number_3.png[] It uses a javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver[] to create, sign and serialize a `<saml2:LogoutResponse>`.
- It uses the keys and configuration from the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`] derived from the endpoint or from the contents of the `<saml2:LogoutRequest>`.
- Then, it redirect-POSTs the `<saml2:LogoutResponse>` to the asserting party SLO endpoint.
- The browser hands control over to the asserting party.
- image:{icondir}/number_4.png[] If invalid, then it https://github.com/spring-projects/spring-security/pull/14676[responds with a 400].
- == Configuring Logout Endpoints
- There are three behaviors that can be triggered by different endpoints:
- * RP-initiated logout, which allows an authenticated user to `POST` and trigger the logout process by sending the asserting party a `<saml2:LogoutRequest>`
- * AP-initiated logout, which allows an asserting party to send a `<saml2:LogoutRequest>` to the application
- * AP logout response, which allows an asserting party to send a `<saml2:LogoutResponse>` in response to the RP-initiated `<saml2:LogoutRequest>`
- The first is triggered by performing normal `POST /logout` when the principal is of type `Saml2AuthenticatedPrincipal`.
- The second is triggered by POSTing to the `/logout/saml2/slo` endpoint with a `SAMLRequest` signed by the asserting party.
- The third is triggered by POSTing to the `/logout/saml2/slo` endpoint with a `SAMLResponse` signed by the asserting party.
- Because the user is already logged in or the original Logout Request is known, the `registrationId` is already known.
- For this reason, `+{registrationId}+` is not part of these URLs by default.
- This URL is customizable in the DSL.
- For example, if you are migrating your existing relying party over to Spring Security, your asserting party may already be pointing to `GET /SLOService.saml2`.
- To reduce changes in configuration for the asserting party, you can configure the filter in the DSL like so:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- http
- .saml2Logout((saml2) -> saml2
- .logoutRequest((request) -> request.logoutUrl("/SLOService.saml2"))
- .logoutResponse((response) -> response.logoutUrl("/SLOService.saml2"))
- );
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- http {
- saml2Logout {
- logoutRequest {
- logoutUrl = "/SLOService.saml2"
- }
- logoutResponse {
- logoutUrl = "/SLOService.saml2"
- }
- }
- }
- ----
- ======
- You should also configure these endpoints in your `RelyingPartyRegistration`.
- Also, you can customize the endpoint for triggering logout locally like so:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- http
- .saml2Logout((saml2) -> saml2.logoutUrl("/saml2/logout"));
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- http {
- saml2Logout {
- logoutUrl = "/saml2/logout"
- }
- }
- ----
- ======
- [[separating-local-saml2-logout]]
- === Separating Local Logout from SAML 2.0 Logout
- In some cases, you may want to expose one logout endpoint for local logout and another for RP-initiated SLO.
- Like is the case with other logout mechanisms, you can register more than one, so long as they each have a different endpoint.
- So, for example, you can wire the DSL like so:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- http
- .logout((logout) -> logout.logoutUrl("/logout"))
- .saml2Logout((saml2) -> saml2.logoutUrl("/saml2/logout"));
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- http {
- logout {
- logoutUrl = "/logout"
- }
- saml2Logout {
- logoutUrl = "/saml2/logout"
- }
- }
- ----
- ======
- and now if a client sends a `POST /logout`, the session will be cleared, but there won't be a `<saml2:LogoutRequest>` sent to the asserting party.
- But, if the client sends a `POST /saml2/logout`, then the application will initiate SAML 2.0 SLO as normal.
- == Customizing `<saml2:LogoutRequest>` Resolution
- It's common to need to set other values in the `<saml2:LogoutRequest>` than the defaults that Spring Security provides.
- By default, Spring Security will issue a `<saml2:LogoutRequest>` and supply:
- * The `DestinationValidator` attribute - from `RelyingPartyRegistration#getAssertingPartyMetadata#getSingleLogoutServiceLocation`
- * The `ID` attribute - a GUID
- * The `<Issuer>` element - from `RelyingPartyRegistration#getEntityId`
- * The `<NameID>` element - from `Authentication#getName`
- To add other values, you can use delegation, like so:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Bean
- Saml2LogoutRequestResolver logoutRequestResolver(RelyingPartyRegistrationRepository registrations) {
- OpenSaml5LogoutRequestResolver logoutRequestResolver =
- new OpenSaml5LogoutRequestResolver(registrations);
- logoutRequestResolver.setParametersConsumer((parameters) -> {
- String name = ((Saml2AuthenticatedPrincipal) parameters.getAuthentication().getPrincipal()).getFirstAttribute("CustomAttribute");
- String format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient";
- LogoutRequest logoutRequest = parameters.getLogoutRequest();
- NameID nameId = logoutRequest.getNameID();
- nameId.setValue(name);
- nameId.setFormat(format);
- });
- return logoutRequestResolver;
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @Bean
- open fun logoutRequestResolver(registrations:RelyingPartyRegistrationRepository?): Saml2LogoutRequestResolver {
- val logoutRequestResolver = OpenSaml5LogoutRequestResolver(registrations)
- logoutRequestResolver.setParametersConsumer { parameters: LogoutRequestParameters ->
- val name: String = (parameters.getAuthentication().getPrincipal() as Saml2AuthenticatedPrincipal).getFirstAttribute("CustomAttribute")
- val format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
- val logoutRequest: LogoutRequest = parameters.getLogoutRequest()
- val nameId: NameID = logoutRequest.getNameID()
- nameId.setValue(name)
- nameId.setFormat(format)
- }
- return logoutRequestResolver
- }
- ----
- ======
- Then, you can supply your custom `Saml2LogoutRequestResolver` in the DSL as follows:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- http
- .saml2Logout((saml2) -> saml2
- .logoutRequest((request) -> request
- .logoutRequestResolver(this.logoutRequestResolver)
- )
- );
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- http {
- saml2Logout {
- logoutRequest {
- logoutRequestResolver = this.logoutRequestResolver
- }
- }
- }
- ----
- ======
- == Customizing `<saml2:LogoutResponse>` Resolution
- It's common to need to set other values in the `<saml2:LogoutResponse>` than the defaults that Spring Security provides.
- By default, Spring Security will issue a `<saml2:LogoutResponse>` and supply:
- * The `DestinationValidator` attribute - from `RelyingPartyRegistration#getAssertingPartyMetadata#getSingleLogoutServiceResponseLocation`
- * The `ID` attribute - a GUID
- * The `<Issuer>` element - from `RelyingPartyRegistration#getEntityId`
- * The `<Status>` element - `SUCCESS`
- To add other values, you can use delegation, like so:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Bean
- public Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) {
- OpenSaml5LogoutResponseResolver logoutRequestResolver =
- new OpenSaml5LogoutResponseResolver(registrations);
- logoutRequestResolver.setParametersConsumer((parameters) -> {
- if (checkOtherPrevailingConditions(parameters.getRequest())) {
- parameters.getLogoutRequest().getStatus().getStatusCode().setCode(StatusCode.PARTIAL_LOGOUT);
- }
- });
- return logoutRequestResolver;
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @Bean
- open fun logoutResponseResolver(registrations: RelyingPartyRegistrationRepository?): Saml2LogoutResponseResolver {
- val logoutRequestResolver = OpenSaml5LogoutResponseResolver(registrations)
- logoutRequestResolver.setParametersConsumer { LogoutResponseParameters parameters ->
- if (checkOtherPrevailingConditions(parameters.getRequest())) {
- parameters.getLogoutRequest().getStatus().getStatusCode().setCode(StatusCode.PARTIAL_LOGOUT)
- }
- }
- return logoutRequestResolver
- }
- ----
- ======
- Then, you can supply your custom `Saml2LogoutResponseResolver` in the DSL as follows:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- http
- .saml2Logout((saml2) -> saml2
- .logoutRequest((request) -> request
- .logoutRequestResolver(this.logoutRequestResolver)
- )
- );
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- http {
- saml2Logout {
- logoutRequest {
- logoutRequestResolver = this.logoutRequestResolver
- }
- }
- }
- ----
- ======
- == Customizing `<saml2:LogoutRequest>` Authentication
- To customize validation, you can implement your own `Saml2LogoutRequestValidator`.
- At this point, the validation is minimal, so you may be able to first delegate to the default `Saml2LogoutRequestValidator` like so:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Component
- public class MyOpenSamlLogoutRequestValidator implements Saml2LogoutRequestValidator {
- private final Saml2LogoutRequestValidator delegate = new OpenSaml5LogoutRequestValidator();
- @Override
- public Saml2LogoutRequestValidator logout(Saml2LogoutRequestValidatorParameters parameters) {
- // verify signature, issuer, destination, and principal name
- Saml2LogoutValidatorResult result = delegate.authenticate(authentication);
- LogoutRequest logoutRequest = // ... parse using OpenSAML
- // perform custom validation
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @Component
- open class MyOpenSamlLogoutRequestValidator: Saml2LogoutRequestValidator {
- private val delegate = OpenSaml5LogoutRequestValidator()
- @Override
- fun logout(parameters: Saml2LogoutRequestValidatorParameters): Saml2LogoutRequestValidator {
- // verify signature, issuer, destination, and principal name
- val result = delegate.authenticate(authentication)
- val logoutRequest: LogoutRequest = // ... parse using OpenSAML
- // perform custom validation
- }
- }
- ----
- ======
- Then, you can supply your custom `Saml2LogoutRequestValidator` in the DSL as follows:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- http
- .saml2Logout((saml2) -> saml2
- .logoutRequest((request) -> request
- .logoutRequestValidator(myOpenSamlLogoutRequestValidator)
- )
- );
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- http {
- saml2Logout {
- logoutRequest {
- logoutRequestValidator = myOpenSamlLogoutRequestValidator
- }
- }
- }
- ----
- ======
- == Customizing `<saml2:LogoutResponse>` Authentication
- To customize validation, you can implement your own `Saml2LogoutResponseValidator`.
- At this point, the validation is minimal, so you may be able to first delegate to the default `Saml2LogoutResponseValidator` like so:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Component
- public class MyOpenSamlLogoutResponseValidator implements Saml2LogoutResponseValidator {
- private final Saml2LogoutResponseValidator delegate = new OpenSaml5LogoutResponseValidator();
- @Override
- public Saml2LogoutValidatorResult logout(Saml2LogoutResponseValidatorParameters parameters) {
- // verify signature, issuer, destination, and status
- Saml2LogoutValidatorResult result = delegate.authenticate(parameters);
- LogoutResponse logoutResponse = // ... parse using OpenSAML
- // perform custom validation
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @Component
- open class MyOpenSamlLogoutResponseValidator: Saml2LogoutResponseValidator {
- private val delegate = OpenSaml5LogoutResponseValidator()
- @Override
- fun logout(parameters: Saml2LogoutResponseValidatorParameters): Saml2LogoutResponseValidator {
- // verify signature, issuer, destination, and status
- val result = delegate.authenticate(authentication)
- val logoutResponse: LogoutResponse = // ... parse using OpenSAML
- // perform custom validation
- }
- }
- ----
- ======
- Then, you can supply your custom `Saml2LogoutResponseValidator` in the DSL as follows:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- http
- .saml2Logout((saml2) -> saml2
- .logoutResponse((response) -> response
- .logoutResponseAuthenticator(myOpenSamlLogoutResponseAuthenticator)
- )
- );
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- http {
- saml2Logout {
- logoutResponse {
- logoutResponseValidator = myOpenSamlLogoutResponseValidator
- }
- }
- }
- ----
- ======
- == Customizing `<saml2:LogoutRequest>` storage
- When your application sends a `<saml2:LogoutRequest>`, the value is stored in the session so that the `RelayState` parameter and the `InResponseTo` attribute in the `<saml2:LogoutResponse>` can be verified.
- If you want to store logout requests in some place other than the session, you can supply your custom implementation in the DSL, like so:
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- http
- .saml2Logout((saml2) -> saml2
- .logoutRequest((request) -> request
- .logoutRequestRepository(myCustomLogoutRequestRepository)
- )
- );
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- http {
- saml2Logout {
- logoutRequest {
- logoutRequestRepository = myCustomLogoutRequestRepository
- }
- }
- }
- ----
- ======
- [[jc-logout-references]]
- == Further Logout-Related References
- - xref:servlet/test/mockmvc/logout.adoc#test-logout[Testing Logout]
- - xref:servlet/integrations/servlet-api.adoc#servletapi-logout[HttpServletRequest.logout()]
- - xref:servlet/exploits/csrf.adoc#csrf-considerations-logout[Logging Out] in section CSRF Caveats
|