| 
					
				 | 
			
			
				@@ -1,7 +1,7 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 [[servlet-saml2login-logout]] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 = Performing Single Logout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-Spring Security ships with support for RP- and AP-initiated SAML 2.0 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: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -22,61 +22,201 @@ To use Spring Security's SAML 2.0 Single Logout feature, you will need the follo 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 * 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 begin from the initial minimal example and add the following configuration: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+You can achieve this in Spring Boot in the following way: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,yaml] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-@Value("${private.key}") RSAPrivateKey key; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-@Value("${public.certificate}") X509Certificate certificate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-@Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-RelyingPartyRegistrationRepository registrations() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    Saml2X509Credential credential = Saml2X509Credential.signing(key, certificate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    RelyingPartyRegistration registration = RelyingPartyRegistrations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .fromMetadataLocation("https://ap.example.org/metadata") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .registrationId("id") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .singleLogoutServiceLocation("{baseUrl}/logout/saml2/slo") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .signingX509Credentials((signing) -> signing.add(credential)) <1> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .build(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return new InMemoryRelyingPartyRegistrationRepository(registration); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-@Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-SecurityFilterChain web(HttpSecurity http, RelyingPartyRegistrationRepository registrations) throws Exception { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .authorizeHttpRequests((authorize) -> authorize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .anyRequest().authenticated() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .saml2Login(withDefaults()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .saml2Logout(withDefaults()); <2> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<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 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return http.build(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[NOTE] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-<1> - First, add your signing key to the `RelyingPartyRegistration` instance or to xref:servlet/saml2/login/overview.adoc#servlet-saml2login-rpr-duplicated[multiple instances] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-<2> - Second, indicate that your application wants to use SAML SLO to logout the end user 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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. Use a `Saml2LogoutRequestResolver` to create, sign, and serialize a `<saml2:LogoutRequest>` based on the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`] associated with the currently logged-in user. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-3. Send a redirect or post to the asserting party based on the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-4. Deserialize, verify, and process the `<saml2:LogoutResponse>` sent by the asserting party 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-5. Redirect to any configured successful logout endpoint 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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`: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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. Use a `Saml2LogoutRequestHandler` to deserialize, verify, and process the `<saml2:LogoutRequest>` sent by the asserting party 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+1. Verify the `<saml2:LogoutRequest>` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 2. Logout the user and invalidate the session 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-3. Create, sign, and serialize a `<saml2:LogoutResponse>` based on the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`] associated with the just logged-out user 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-4. Send a redirect or post to the asserting party based on the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-NOTE: Adding `saml2Logout` adds the capability for logout to the service provider. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @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 can do this by setting the `RelyingPartyRegistration.Builder#singleLogoutServiceLocation` property. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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 http://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 {security-api-url}org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2RelyingPartyInitiatedLogoutSuccessHandler.html[`Saml2RelyingPartyInitiatedLogoutSuccessHandler`]. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+image:{icondir}/number_2.png[] The logout success handler uses an instance of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{security-api-url}org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestResolver.html[`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 {security-api-url}org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseFilter.html[`Saml2LogoutResponseFilter`] deserializes, verifies, and processes the `<saml2:LogoutResponse>` with its {security-api-url}org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutResponseValidator.html[`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 {security-api-url}org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.html[`Saml2LogoutRequestFilter`] deserializes, verifies, and processes the `<saml2:LogoutRequest>` with its {security-api-url}org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutRequestValidator.html[`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 {security-api-url}org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.html[`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 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -112,10 +252,87 @@ http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         .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. 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -129,7 +346,11 @@ By default, Spring Security will issue a `<saml2:LogoutRequest>` and supply: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 To add other values, you can use delegation, like so: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[tabs] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+====== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Java:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 @Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 Saml2LogoutRequestResolver logoutRequestResolver(RelyingPartyRegistrationRepository registrations) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -147,9 +368,33 @@ Saml2LogoutRequestResolver logoutRequestResolver(RelyingPartyRegistrationReposit 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Kotlin:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,kotlin,role="secondary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+open fun logoutRequestResolver(registrations:RelyingPartyRegistrationRepository?): Saml2LogoutRequestResolver { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    val logoutRequestResolver = OpenSaml4LogoutRequestResolver(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: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[tabs] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+====== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Java:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     .saml2Logout((saml2) -> saml2 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -159,6 +404,20 @@ http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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. 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -172,7 +431,11 @@ By default, Spring Security will issue a `<saml2:LogoutResponse>` and supply: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 To add other values, you can use delegation, like so: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[tabs] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+====== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Java:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 @Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 public Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -187,9 +450,30 @@ public Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrati 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Kotlin:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,kotlin,role="secondary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+open fun logoutResponseResolver(registrations: RelyingPartyRegistrationRepository?): Saml2LogoutResponseResolver { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    val logoutRequestResolver = OpenSaml4LogoutResponseResolver(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: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[tabs] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+====== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Java:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     .saml2Logout((saml2) -> saml2 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -199,12 +483,30 @@ http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[tabs] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+====== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Java:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 @Component 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 public class MyOpenSamlLogoutRequestValidator implements Saml2LogoutRequestValidator { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -221,24 +523,66 @@ public class MyOpenSamlLogoutRequestValidator implements Saml2LogoutRequestValid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Kotlin:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,kotlin,role="secondary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Component 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+open class MyOpenSamlLogoutRequestValidator: Saml2LogoutRequestValidator { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private val delegate = OpenSamlLogoutRequestValidator() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	@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: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[tabs] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+====== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Java:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     .saml2Logout((saml2) -> saml2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         .logoutRequest((request) -> request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .logoutRequestAuthenticator(myOpenSamlLogoutRequestAuthenticator) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .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: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[tabs] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+====== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Java:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 @Component 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 public class MyOpenSamlLogoutResponseValidator implements Saml2LogoutResponseValidator { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -255,9 +599,33 @@ public class MyOpenSamlLogoutResponseValidator implements Saml2LogoutResponseVal 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Kotlin:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,kotlin,role="secondary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Component 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+open class MyOpenSamlLogoutResponseValidator: Saml2LogoutResponseValidator { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private val delegate = OpenSamlLogoutResponseValidator() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	@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: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[tabs] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+====== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Java:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     .saml2Logout((saml2) -> saml2 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -267,13 +635,31 @@ http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[tabs] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+====== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Java:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     .saml2Logout((saml2) -> saml2 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -282,3 +668,24 @@ http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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 
			 |