diff --git a/config/src/main/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/OpaqueTokenDsl.kt b/config/src/main/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/OpaqueTokenDsl.kt index f912379654..5c8ab0c3f5 100644 --- a/config/src/main/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/OpaqueTokenDsl.kt +++ b/config/src/main/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/OpaqueTokenDsl.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. @@ -16,8 +16,10 @@ package org.springframework.security.config.web.servlet.oauth2.resourceserver +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.server.resource.introspection.OpaqueTokenIntrospector /** @@ -27,6 +29,8 @@ import org.springframework.security.oauth2.server.resource.introspection.OpaqueT * @since 5.3 * @property introspectionUri the URI of the Introspection endpoint. * @property introspector the [OpaqueTokenIntrospector] to use. + * @property authenticationManager the [AuthenticationManager] used to determine if the provided + * [Authentication] can be authenticated. */ @OAuth2ResourceServerSecurityMarker class OpaqueTokenDsl { @@ -34,6 +38,8 @@ class OpaqueTokenDsl { private var _introspector: OpaqueTokenIntrospector? = null private var clientCredentials: Pair? = null + var authenticationManager: AuthenticationManager? = null + var introspectionUri: String? get() = _introspectionUri set(value) { @@ -65,6 +71,7 @@ class OpaqueTokenDsl { introspectionUri?.also { opaqueToken.introspectionUri(introspectionUri) } introspector?.also { opaqueToken.introspector(introspector) } clientCredentials?.also { opaqueToken.introspectionClientCredentials(clientCredentials!!.first, clientCredentials!!.second) } + authenticationManager?.also { opaqueToken.authenticationManager(authenticationManager) } } } } diff --git a/config/src/test/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/OpaqueTokenDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/OpaqueTokenDslTests.kt index fff9f378a5..b0a03210ad 100644 --- a/config/src/test/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/OpaqueTokenDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/web/servlet/oauth2/resourceserver/OpaqueTokenDslTests.kt @@ -27,6 +27,9 @@ import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.http.ResponseEntity +import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.authentication.ProviderManager +import org.springframework.security.authentication.TestingAuthenticationProvider 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 @@ -34,7 +37,9 @@ 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.DefaultOAuth2AuthenticatedPrincipal +import org.springframework.security.oauth2.core.TestOAuth2AccessTokens import org.springframework.security.oauth2.jwt.JwtClaimNames +import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector import org.springframework.test.web.servlet.MockMvc @@ -57,6 +62,18 @@ class OpaqueTokenDslTests { @Autowired lateinit var mockMvc: MockMvc + private val introspectionAuthenticationToken = BearerTokenAuthentication( + DefaultOAuth2AuthenticatedPrincipal( + mapOf( + Pair( + JwtClaimNames.SUB, + "mock-test-subject" + ) + ), emptyList() + ), + TestOAuth2AccessTokens.noScopes(), emptyList() + ) + @Test fun `opaque token when defaults then uses introspection`() { this.spring.register(DefaultOpaqueConfig::class.java, AuthenticationController::class.java).autowire() @@ -188,6 +205,45 @@ class OpaqueTokenDslTests { } } + @Test + fun `opaque token 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.introspectionAuthenticationToken + + 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 { + opaqueToken { + authenticationManager = AUTHENTICATION_MANAGER + } + } + } + } + } + @RestController class AuthenticationController { @GetMapping("/authenticated")