Skip to content

Commit

Permalink
Add Kotlin Oidc SessionLogout Support
Browse files Browse the repository at this point in the history
  • Loading branch information
jzheaux committed Sep 4, 2024
1 parent 4c245d9 commit 8545b53
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

/*
* Copyright 2002-2023 the original author or authors.
*
Expand Down Expand Up @@ -72,4 +73,5 @@ class OidcLogoutDsl {
backChannel?.also { oidcLogout.backChannel(backChannel) }
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,42 @@ import org.springframework.security.config.annotation.web.configurers.oauth2.cli
*/
@OAuth2LoginSecurityMarker
class OidcBackChannelLogoutDsl {

private var sessionLogout: ((OidcLogoutConfigurer<HttpSecurity>.BackChannelLogoutConfigurer.SessionLogoutConfigurer) -> Unit)? = null

/**
* Configures the OIDC 1.0 Back-Channel endpoint.
*
* Example:
*
* ```
* @Configuration
* @EnableWebSecurity
* class SecurityConfig {
*
* @Bean
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
* http {
* oauth2Login { }
* oidcLogout {
* backChannel { }
* }
* }
* return http.build()
* }
* }
* ```
*
* @param backChannelConfig custom configurations to configure the back-channel endpoint
* @see [OidcBackChannelLogoutDsl]
*/
fun sessionLogout(sessionLogoutConfig: OidcSessionLogoutDsl.() -> Unit) {
this.sessionLogout = OidcSessionLogoutDsl().apply(sessionLogoutConfig).get()
}

internal fun get(): (OidcLogoutConfigurer<HttpSecurity>.BackChannelLogoutConfigurer) -> Unit {
return { backChannel -> }
return { backChannel ->
sessionLogout?.also { backChannel.sessionLogout(sessionLogout) }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2002-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.security.config.annotation.web.oauth2.login

import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer

/**
* A Kotlin DSL to configure the OIDC 1.0 Back-Channel configuration using
* idiomatic Kotlin code.
*
* @author Josh Cummings
* @since 6.2
*/
@OAuth2LoginSecurityMarker
class OidcSessionLogoutDsl {
var uri: String = "{baseUrl}/logout/connect/back-channel/{registrationId}"
var cookieName: String = "JSESSIONID"

internal fun get(): (OidcLogoutConfigurer<HttpSecurity>.BackChannelLogoutConfigurer.SessionLogoutConfigurer) -> Unit {
return { sessionLogout ->
uri?.also { sessionLogout.uri(uri) }
cookieName?.also { sessionLogout.cookieName(cookieName) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ package org.springframework.security.config.web.server
*/
@ServerSecurityMarker
class ServerOidcBackChannelLogoutDsl {
private var sessionLogout: ((ServerHttpSecurity.OidcLogoutSpec.BackChannelLogoutConfigurer.SessionLogoutConfigurer) -> Unit)? = null

fun sessionLogout(sessionLogoutConfig: ServerOidcSessionLogoutDsl.() -> Unit) {
this.sessionLogout = ServerOidcSessionLogoutDsl().apply(sessionLogoutConfig).get()
}


internal fun get(): (ServerHttpSecurity.OidcLogoutSpec.BackChannelLogoutConfigurer) -> Unit {
return { backChannel -> }
return { backChannel ->
sessionLogout?.also { backChannel.sessionLogout(sessionLogout) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ class ServerOidcLogoutDsl {
* return http {
* oauth2Login { }
* oidcLogout {
* backChannel { }
* backChannel {
* sessionLogout { }
* }
* }
* }
* }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2002-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.security.config.web.server

/**
* A Kotlin DSL to configure [ServerHttpSecurity] OIDC 1.0 Back-Channel Logout support using idiomatic Kotlin code.
*
* @author Josh Cummings
* @since 6.2
*/
@ServerSecurityMarker
class ServerOidcSessionLogoutDsl {
var uri: String = "{baseUrl}/logout/connect/back-channel/{registrationId}"
var cookieName: String = "JSESSIONID"

internal fun get(): (ServerHttpSecurity.OidcLogoutSpec.BackChannelLogoutConfigurer.SessionLogoutConfigurer) -> Unit {
return { sessionLogout ->
uri?.also { sessionLogout.uri(uri) }
cookieName?.also { sessionLogout.cookieName(cookieName) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository
import org.springframework.security.oauth2.client.registration.TestClientRegistrations
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.logout.LogoutHandler
import org.springframework.test.util.ReflectionTestUtils
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.post

Expand All @@ -53,6 +55,15 @@ class OidcLogoutDslTests {
this.mockMvc.post("/logout/connect/back-channel/" + clientRegistration.registrationId) {
param("logout_token", "token")
}.andExpect { status { isBadRequest() } }
val chain: SecurityFilterChain = this.spring.context.getBean(SecurityFilterChain::class.java)
for (filter in chain.filters) {
if (filter.javaClass.simpleName.equals("OidcBackChannelLogoutFilter")) {
val logoutHandler = ReflectionTestUtils.getField(filter, "logoutHandler") as LogoutHandler
val backChannelLogoutHandler = ReflectionTestUtils.getField(logoutHandler, "left") as LogoutHandler
var cookieName = ReflectionTestUtils.getField(backChannelLogoutHandler, "sessionCookieName") as String
assert(cookieName.equals("SESSION"))
}
}
}

@Configuration
Expand All @@ -64,7 +75,11 @@ class OidcLogoutDslTests {
http {
oauth2Login { }
oidcLogout {
backChannel { }
backChannel {
sessionLogout {
cookieName = "SESSION"
}
}
}
authorizeHttpRequests {
authorize(anyRequest, authenticated)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository
import org.springframework.security.oauth2.client.registration.TestClientRegistrations
import org.springframework.security.web.authentication.logout.LogoutHandler
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.test.util.ReflectionTestUtils
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.web.reactive.config.EnableWebFlux
import org.springframework.web.reactive.function.BodyInserters
import org.springframework.web.server.WebFilter

/**
* Tests for [ServerOidcLogoutDsl]
Expand Down Expand Up @@ -63,6 +66,15 @@ class ServerOidcLogoutDslTests {
.body(BodyInserters.fromFormData("logout_token", "token"))
.exchange()
.expectStatus().isBadRequest
val chain: SecurityWebFilterChain = this.spring.context.getBean(SecurityWebFilterChain::class.java)
chain.webFilters.doOnNext({ filter: WebFilter ->
if (filter.javaClass.simpleName.equals("OidcBackChannelLogoutWebFilter")) {
val logoutHandler = ReflectionTestUtils.getField(filter, "logoutHandler") as LogoutHandler
val backChannelLogoutHandler = ReflectionTestUtils.getField(logoutHandler, "left") as LogoutHandler
var cookieName = ReflectionTestUtils.getField(backChannelLogoutHandler, "sessionCookieName") as String
assert(cookieName.equals("SESSION"))
}
})
}

@Configuration
Expand Down

0 comments on commit 8545b53

Please sign in to comment.