Skip to content

Commit 8545b53

Browse files
committed
Add Kotlin Oidc SessionLogout Support
Issue gh-14904 Issue gh-13841
1 parent 4c245d9 commit 8545b53

File tree

8 files changed

+155
-4
lines changed

8 files changed

+155
-4
lines changed

config/src/main/kotlin/org/springframework/security/config/annotation/web/OidcLogoutDsl.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
/*
23
* Copyright 2002-2023 the original author or authors.
34
*
@@ -72,4 +73,5 @@ class OidcLogoutDsl {
7273
backChannel?.also { oidcLogout.backChannel(backChannel) }
7374
}
7475
}
76+
7577
}

config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/login/OidcBackChannelLogoutDsl.kt

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,42 @@ import org.springframework.security.config.annotation.web.configurers.oauth2.cli
2828
*/
2929
@OAuth2LoginSecurityMarker
3030
class OidcBackChannelLogoutDsl {
31+
32+
private var sessionLogout: ((OidcLogoutConfigurer<HttpSecurity>.BackChannelLogoutConfigurer.SessionLogoutConfigurer) -> Unit)? = null
33+
34+
/**
35+
* Configures the OIDC 1.0 Back-Channel endpoint.
36+
*
37+
* Example:
38+
*
39+
* ```
40+
* @Configuration
41+
* @EnableWebSecurity
42+
* class SecurityConfig {
43+
*
44+
* @Bean
45+
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
46+
* http {
47+
* oauth2Login { }
48+
* oidcLogout {
49+
* backChannel { }
50+
* }
51+
* }
52+
* return http.build()
53+
* }
54+
* }
55+
* ```
56+
*
57+
* @param backChannelConfig custom configurations to configure the back-channel endpoint
58+
* @see [OidcBackChannelLogoutDsl]
59+
*/
60+
fun sessionLogout(sessionLogoutConfig: OidcSessionLogoutDsl.() -> Unit) {
61+
this.sessionLogout = OidcSessionLogoutDsl().apply(sessionLogoutConfig).get()
62+
}
63+
3164
internal fun get(): (OidcLogoutConfigurer<HttpSecurity>.BackChannelLogoutConfigurer) -> Unit {
32-
return { backChannel -> }
65+
return { backChannel ->
66+
sessionLogout?.also { backChannel.sessionLogout(sessionLogout) }
67+
}
3368
}
3469
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2002-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.web.oauth2.login
18+
19+
import org.springframework.security.config.annotation.web.builders.HttpSecurity
20+
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer
21+
22+
/**
23+
* A Kotlin DSL to configure the OIDC 1.0 Back-Channel configuration using
24+
* idiomatic Kotlin code.
25+
*
26+
* @author Josh Cummings
27+
* @since 6.2
28+
*/
29+
@OAuth2LoginSecurityMarker
30+
class OidcSessionLogoutDsl {
31+
var uri: String = "{baseUrl}/logout/connect/back-channel/{registrationId}"
32+
var cookieName: String = "JSESSIONID"
33+
34+
internal fun get(): (OidcLogoutConfigurer<HttpSecurity>.BackChannelLogoutConfigurer.SessionLogoutConfigurer) -> Unit {
35+
return { sessionLogout ->
36+
uri?.also { sessionLogout.uri(uri) }
37+
cookieName?.also { sessionLogout.cookieName(cookieName) }
38+
}
39+
}
40+
}

config/src/main/kotlin/org/springframework/security/config/web/server/ServerOidcBackChannelLogoutDsl.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,16 @@ package org.springframework.security.config.web.server
2424
*/
2525
@ServerSecurityMarker
2626
class ServerOidcBackChannelLogoutDsl {
27+
private var sessionLogout: ((ServerHttpSecurity.OidcLogoutSpec.BackChannelLogoutConfigurer.SessionLogoutConfigurer) -> Unit)? = null
28+
29+
fun sessionLogout(sessionLogoutConfig: ServerOidcSessionLogoutDsl.() -> Unit) {
30+
this.sessionLogout = ServerOidcSessionLogoutDsl().apply(sessionLogoutConfig).get()
31+
}
32+
33+
2734
internal fun get(): (ServerHttpSecurity.OidcLogoutSpec.BackChannelLogoutConfigurer) -> Unit {
28-
return { backChannel -> }
35+
return { backChannel ->
36+
sessionLogout?.also { backChannel.sessionLogout(sessionLogout) }
37+
}
2938
}
3039
}

config/src/main/kotlin/org/springframework/security/config/web/server/ServerOidcLogoutDsl.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ class ServerOidcLogoutDsl {
4747
* return http {
4848
* oauth2Login { }
4949
* oidcLogout {
50-
* backChannel { }
50+
* backChannel {
51+
* sessionLogout { }
52+
* }
5153
* }
5254
* }
5355
* }
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2002-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.web.server
18+
19+
/**
20+
* A Kotlin DSL to configure [ServerHttpSecurity] OIDC 1.0 Back-Channel Logout support using idiomatic Kotlin code.
21+
*
22+
* @author Josh Cummings
23+
* @since 6.2
24+
*/
25+
@ServerSecurityMarker
26+
class ServerOidcSessionLogoutDsl {
27+
var uri: String = "{baseUrl}/logout/connect/back-channel/{registrationId}"
28+
var cookieName: String = "JSESSIONID"
29+
30+
internal fun get(): (ServerHttpSecurity.OidcLogoutSpec.BackChannelLogoutConfigurer.SessionLogoutConfigurer) -> Unit {
31+
return { sessionLogout ->
32+
uri?.also { sessionLogout.uri(uri) }
33+
cookieName?.also { sessionLogout.cookieName(cookieName) }
34+
}
35+
}
36+
}

config/src/test/kotlin/org/springframework/security/config/annotation/web/OidcLogoutDslTests.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
3030
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository
3131
import org.springframework.security.oauth2.client.registration.TestClientRegistrations
3232
import org.springframework.security.web.SecurityFilterChain
33+
import org.springframework.security.web.authentication.logout.LogoutHandler
34+
import org.springframework.test.util.ReflectionTestUtils
3335
import org.springframework.test.web.servlet.MockMvc
3436
import org.springframework.test.web.servlet.post
3537

@@ -53,6 +55,15 @@ class OidcLogoutDslTests {
5355
this.mockMvc.post("/logout/connect/back-channel/" + clientRegistration.registrationId) {
5456
param("logout_token", "token")
5557
}.andExpect { status { isBadRequest() } }
58+
val chain: SecurityFilterChain = this.spring.context.getBean(SecurityFilterChain::class.java)
59+
for (filter in chain.filters) {
60+
if (filter.javaClass.simpleName.equals("OidcBackChannelLogoutFilter")) {
61+
val logoutHandler = ReflectionTestUtils.getField(filter, "logoutHandler") as LogoutHandler
62+
val backChannelLogoutHandler = ReflectionTestUtils.getField(logoutHandler, "left") as LogoutHandler
63+
var cookieName = ReflectionTestUtils.getField(backChannelLogoutHandler, "sessionCookieName") as String
64+
assert(cookieName.equals("SESSION"))
65+
}
66+
}
5667
}
5768

5869
@Configuration
@@ -64,7 +75,11 @@ class OidcLogoutDslTests {
6475
http {
6576
oauth2Login { }
6677
oidcLogout {
67-
backChannel { }
78+
backChannel {
79+
sessionLogout {
80+
cookieName = "SESSION"
81+
}
82+
}
6883
}
6984
authorizeHttpRequests {
7085
authorize(anyRequest, authenticated)

config/src/test/kotlin/org/springframework/security/config/web/server/ServerOidcLogoutDslTests.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
2929
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository
3030
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository
3131
import org.springframework.security.oauth2.client.registration.TestClientRegistrations
32+
import org.springframework.security.web.authentication.logout.LogoutHandler
3233
import org.springframework.security.web.server.SecurityWebFilterChain
34+
import org.springframework.test.util.ReflectionTestUtils
3335
import org.springframework.test.web.reactive.server.WebTestClient
3436
import org.springframework.web.reactive.config.EnableWebFlux
3537
import org.springframework.web.reactive.function.BodyInserters
38+
import org.springframework.web.server.WebFilter
3639

3740
/**
3841
* Tests for [ServerOidcLogoutDsl]
@@ -63,6 +66,15 @@ class ServerOidcLogoutDslTests {
6366
.body(BodyInserters.fromFormData("logout_token", "token"))
6467
.exchange()
6568
.expectStatus().isBadRequest
69+
val chain: SecurityWebFilterChain = this.spring.context.getBean(SecurityWebFilterChain::class.java)
70+
chain.webFilters.doOnNext({ filter: WebFilter ->
71+
if (filter.javaClass.simpleName.equals("OidcBackChannelLogoutWebFilter")) {
72+
val logoutHandler = ReflectionTestUtils.getField(filter, "logoutHandler") as LogoutHandler
73+
val backChannelLogoutHandler = ReflectionTestUtils.getField(logoutHandler, "left") as LogoutHandler
74+
var cookieName = ReflectionTestUtils.getField(backChannelLogoutHandler, "sessionCookieName") as String
75+
assert(cookieName.equals("SESSION"))
76+
}
77+
})
6678
}
6779

6880
@Configuration

0 commit comments

Comments
 (0)