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 + } + } + }