checking https://docs.spring.io/spring-security/reference/servlet/test/mockmvc/oauth2.html I see no way to test client credentials without the servlet context.
I wrote this test
package com.capitalone.e1.configuration.feign
import com.fasterxml.jackson.databind.ObjectMapper
import feign.RequestTemplate
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.mockito.ArgumentMatchers.matches
import org.mockito.kotlin.mock
import org.mockito.kotlin.same
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockserver.integration.ClientAndServer
import org.mockserver.model.HttpRequest
import org.mockserver.model.HttpResponse
import org.mockserver.model.MediaType
import org.mockserver.springtest.MockServerTest
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.context.annotation.Bean
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpMethod
import org.springframework.security.oauth2.core.OAuth2AccessToken
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse
import org.springframework.web.bind.annotation.GetMapping
import java.time.Duration
import java.util.UUID
@SpringBootTest
@MockServerTest
internal class DevExchangeOauth2FeignRequestInterceptorTest @Autowired constructor(
private val fooRepo: FooRepo,
private val mapper: ObjectMapper
) {
@AfterEach
fun after( @Autowired testFakes: ClientAndServer ) {
testFakes.reset()
}
@Test
fun oauth( @Autowired interceptor: DevExchangeOAuth2FeignRequestInterceptor, @Autowired fakes: ClientAndServer ) {
val token = OAuth2AccessTokenResponse.withToken(UUID.randomUUID().toString())
.tokenType(OAuth2AccessToken.TokenType.BEARER)
.expiresIn(Duration.ofMillis(10).toMillis())
.build()
fakes.`when`(
HttpRequest.request()
.withMethod(HttpMethod.POST.name)
.withPath("/oauth2/token")
).respond(
HttpResponse.response()
.withStatusCode(200)
.withBody(mapper.writeValueAsString(token), MediaType.APPLICATION_JSON)
)
val template = mock<RequestTemplate>()
interceptor.apply(template)
verify(template, times(1))
.header(same(HttpHeaders.AUTHORIZATION), matches("^Bearer [A-Za-z\\d]+"))
}
@FeignClient(name = "devexchange-v1", url = "\${cof.devexchange.url}/foo")
interface FooRepo {
@GetMapping
fun findAll(): List<Any>
}
@TestConfiguration
internal open class Config {
@Bean
open fun clientAndServer(@Value("\${mockServerPort}") mockServerPort: Int) = ClientAndServer(mockServerPort)
}
}
for this implementation
package com.capitalone.e1.configuration.feign
import feign.RequestInterceptor
import feign.RequestTemplate
import org.springframework.http.HttpHeaders
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager
import org.springframework.stereotype.Component
@Component
class DevExchangeOAuth2FeignRequestInterceptor(
private val manager: OAuth2AuthorizedClientManager
) : RequestInterceptor {
override fun apply(template: RequestTemplate) {
val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("devexchange")
.principal("devexchange")
.build()
val client = manager.authorize(authorizeRequest)!!
template.header(HttpHeaders.AUTHORIZATION, "${client.accessToken.tokenType.value} ${client.accessToken.tokenValue}")
}
}
but I get this strange error, which lies. It says tokenValue cannot be empty, it isn't. token_value is though, which appears to be what it's looking for.
Caused by: java.lang.IllegalArgumentException: tokenValue cannot be empty
at org.springframework.util.Assert.hasText(Assert.java:289)
at org.springframework.security.oauth2.core.AbstractOAuth2Token.<init>(AbstractOAuth2Token.java:61)
at org.springframework.security.oauth2.core.OAuth2AccessToken.<init>(OAuth2AccessToken.java:71)
at org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse$Builder.build(OAuth2AccessTokenResponse.java:191)
at org.springframework.security.oauth2.core.endpoint.DefaultMapOAuth2AccessTokenResponseConverter.convert(DefaultMapOAuth2AccessTokenResponseConverter.java:64)
at org.springframework.security.oauth2.core.endpoint.DefaultMapOAuth2AccessTokenResponseConverter.convert(DefaultMapOAuth2AccessTokenResponseConverter.java:37)
at org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter.readInternal(OAuth2AccessTokenResponseHttpMessageConverter.java:102)
I've found no docs on how to test this properly. The docs seem obsessed with servlet.
Comment From: sjohnr
Hi @xenoterracide!
I've found no docs on how to test this properly.
I understand that you're testing something related to client_credentials, but it's not clear exactly what your goal is. Can you please elaborate on your use case? For example, are you trying to mock out an authorization server's token endpoint?
but I get this strange error
Please provide a minimal, reproducible sample so that we can debug the issue. The sample will ideally not include any dependencies (e.g. feign) other than Spring Security (though a minimal Spring Boot application would be fine).
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: spring-projects-issues
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.