diff --git a/config/src/main/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/JwtDsl.kt b/config/src/main/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/JwtDsl.kt
index e8d8008a97..a3b5cbc71a 100644
--- a/config/src/main/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/JwtDsl.kt
+++ b/config/src/main/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/JwtDsl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -18,8 +18,10 @@ package org.springframework.security.config.web.servlet.oauth2.resourceserver
import org.springframework.core.convert.converter.Converter
import org.springframework.security.authentication.AbstractAuthenticationToken
+import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer
+import org.springframework.security.core.Authentication
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.jwt.JwtDecoder
@@ -33,12 +35,16 @@ import org.springframework.security.oauth2.jwt.JwtDecoder
* @property jwtDecoder the [JwtDecoder] to use.
* @property jwkSetUri configures a [JwtDecoder] using a
* JSON Web Key (JWK) URL
+ * @property authenticationManager the [AuthenticationManager] used to determine if the provided
+ * [Authentication] can be authenticated.
*/
@OAuth2ResourceServerSecurityMarker
class JwtDsl {
private var _jwtDecoder: JwtDecoder? = null
private var _jwkSetUri: String? = null
+ var authenticationManager: AuthenticationManager? = null
+
var jwtAuthenticationConverter: Converter? = null
var jwtDecoder: JwtDecoder?
get() = _jwtDecoder
@@ -58,6 +64,7 @@ class JwtDsl {
jwtAuthenticationConverter?.also { jwt.jwtAuthenticationConverter(jwtAuthenticationConverter) }
jwtDecoder?.also { jwt.decoder(jwtDecoder) }
jwkSetUri?.also { jwt.jwkSetUri(jwkSetUri) }
+ authenticationManager?.also { jwt.authenticationManager(authenticationManager) }
}
}
}
diff --git a/config/src/test/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/JwtDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/JwtDslTests.kt
index b6b5c1367e..f63d38beb1 100644
--- a/config/src/test/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/JwtDslTests.kt
+++ b/config/src/test/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/JwtDslTests.kt
@@ -26,17 +26,24 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.core.convert.converter.Converter
import org.springframework.security.authentication.AbstractAuthenticationToken
+import org.springframework.security.authentication.AuthenticationManager
+import org.springframework.security.authentication.ProviderManager
+import org.springframework.security.authentication.TestingAuthenticationProvider
import org.springframework.security.authentication.TestingAuthenticationToken
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.test.SpringTestRule
import org.springframework.security.config.web.servlet.invoke
+import org.springframework.security.core.Authentication
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.jwt.JwtDecoder
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RestController
/**
* Tests for [JwtDsl]
@@ -44,6 +51,16 @@ import org.springframework.test.web.servlet.get
* @author Eleftheria Stein
*/
class JwtDslTests {
+
+ private val jwtAuthenticationToken: Authentication = JwtAuthenticationToken(
+ Jwt.withTokenValue("token")
+ .header("alg", "none")
+ .claim(IdTokenClaimNames.SUB, "user")
+ .subject("mock-test-subject")
+ .build(),
+ emptyList()
+ )
+
@Rule
@JvmField
val spring = SpringTestRule()
@@ -174,4 +191,52 @@ class JwtDslTests {
}
}
}
+
+ @Test
+ fun `JWT when custom authentication manager configured then used`() {
+ this.spring.register(AuthenticationManagerConfig::class.java, AuthenticationController::class.java).autowire()
+ mockkObject(AuthenticationManagerConfig.AUTHENTICATION_MANAGER)
+ every {
+ AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any())
+ } returns this.jwtAuthenticationToken
+
+ this.mockMvc.get("/authenticated") {
+ header("Authorization", "Bearer token")
+ }.andExpect {
+ status { isOk() }
+ content { string("mock-test-subject") }
+ }
+
+ verify(exactly = 1) { AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }
+ }
+
+ @EnableWebSecurity
+ open class AuthenticationManagerConfig : WebSecurityConfigurerAdapter() {
+
+ companion object {
+ val AUTHENTICATION_MANAGER: AuthenticationManager = ProviderManager(TestingAuthenticationProvider())
+ }
+
+ override fun configure(http: HttpSecurity) {
+ http {
+ authorizeRequests {
+ authorize(anyRequest, authenticated)
+ }
+ oauth2ResourceServer {
+ jwt {
+ authenticationManager = AUTHENTICATION_MANAGER
+ }
+ }
+ }
+ }
+ }
+
+ @RestController
+ class AuthenticationController {
+ @GetMapping("/authenticated")
+ fun authenticated(authentication: Authentication): String {
+ return authentication.name
+ }
+ }
+
}