Search code examples
spring-bootspring-securityoauth-2.0jwtauth0

All endpoints return 401/403 in ControllerTest when adding resourceserver in my Spring Boot project


In these days, we are going to migrate our custom JWT filter to Auth0 IDP.

I was using Spring 2.7.1, WebFlux/Kotlin Coroutines in this project. I created a sample project to demo Reactive Spring Security OAuth2 with Auth0 IDP issue.

BTW, I've maintained a WebMvc version to demo Spring Security OAuth2 and OAuth0(updated to Spring 2.7.1), in which the tests are working well.

When I added org.springframework.boot:spring-boot-starter-oauth2-resource-server to our project deps to enable OAuth2 resource server support in our backend API.

But now all API endpoints are protected and returns 401 or 403.

My security config like this.

@Bean
fun springWebFilterChain(http: ServerHttpSecurity, reactiveJwtDecoder: ReactiveJwtDecoder): SecurityWebFilterChain =
http {

    csrf { disable() }
    httpBasic { disable() }
    formLogin { disable() }
    logout { disable() }

    // enable OAuth2 resource server support
    oauth2ResourceServer { jwt { jwtDecoder = reactiveJwtDecoder } }
    exceptionHandling {
        authenticationEntryPoint = problemSupport
        accessDeniedHandler = problemSupport
    }
    authorizeExchange {
        authorize(pathMatchers(GET, "/v1/me"), authenticated)
        authorize(anyExchange, permitAll)
    }
}

The testing codes is like this, check the complete codes(The tests are ported from other projects, I have not added jwt mock).

@Test
fun `get all posts`() = runTest {
    coEvery { posts.findAll() } returns flowOf(
        Post(
            id = UUID.randomUUID(),
            title = "test title",
            content = "test content"
        )
    )

    client.get()
        .uri("/posts").accept(MediaType.APPLICATION_JSON)
        .exchange()
        .expectStatus().isOk
        .expectBodyList(Post::class.java).hasSize(1)

    coVerify(exactly = 1) { posts.findAll() }
}

The get all posts endpoints is set to permitAll, when running curl http://localhost:8080/posts command in a opening Powershell, it works well and print all posts in the console.

When running test of GET /posts endpoint. I got the following info from console.

2022-07-01 12:45:27.021 DEBUG 14132 --- [           main] o.s.w.r.f.client.ExchangeFunctions       : [6144e499] HTTP GET /posts
2022-07-01 12:45:27.069 DEBUG 14132 --- [     parallel-1] o.s.w.s.adapter.HttpWebHandlerAdapter    : [26193fe] HTTP GET "/posts"
2022-07-01 12:45:27.108 DEBUG 14132 --- [     parallel-1] .s.s.w.s.u.m.AndServerWebExchangeMatcher : Trying to match using org.springframework.security.web.server.csrf.CsrfWebFilter$DefaultRequireCsrfProtectionMatcher@6fc32403
2022-07-01 12:45:27.110 DEBUG 14132 --- [     parallel-1] .s.s.w.s.u.m.AndServerWebExchangeMatcher : Did not match
2022-07-01 12:45:27.146 DEBUG 14132 --- [     parallel-2] o.s.w.s.s.DefaultWebSessionManager       : Created new WebSession.
2022-07-01 12:45:27.151 DEBUG 14132 --- [     parallel-2] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/logout', method=POST}
2022-07-01 12:45:27.151 DEBUG 14132 --- [     parallel-2] athPatternParserServerWebExchangeMatcher : Request 'GET /posts' doesn't match 'POST /logout'
2022-07-01 12:45:27.152 DEBUG 14132 --- [     parallel-2] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found
2022-07-01 12:45:27.156 DEBUG 14132 --- [     parallel-2] a.DelegatingReactiveAuthorizationManager : Checking authorization on '/posts' using org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager@66bdd968
2022-07-01 12:45:27.159 DEBUG 14132 --- [     parallel-2] ebSessionServerSecurityContextRepository : No SecurityContext found in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession@529dcf30'
2022-07-01 12:45:27.160 DEBUG 14132 --- [     parallel-2] o.s.s.w.s.a.AuthorizationWebFilter       : Authorization failed: Access Denied
2022-07-01 12:45:27.174 DEBUG 14132 --- [     parallel-2] ebSessionServerSecurityContextRepository : No SecurityContext found in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession@529dcf30'
2022-07-01 12:45:27.184 DEBUG 14132 --- [     parallel-2] o.s.w.s.adapter.HttpWebHandlerAdapter    : [26193fe] Completed 401 UNAUTHORIZED
2022-07-01 12:45:27.189 DEBUG 14132 --- [     parallel-2] o.s.w.r.f.client.ExchangeFunctions       : [6144e499] [609b7bfb] Response 401 UNAUTHORIZED
2022-07-01 12:45:27.207 ERROR 14132 --- [           main] o.s.t.w.reactive.server.ExchangeResult   : Request details for assertion failure:

> GET /posts
> WebTestClient-Request-Id: [1]
> Accept: [application/json]

No content

< 401 UNAUTHORIZED Unauthorized
< WWW-Authenticate: [Bearer]
< Cache-Control: [no-cache, no-store, max-age=0, must-revalidate]
< Pragma: [no-cache]
< Expires: [0]
< X-Content-Type-Options: [nosniff]
< X-Frame-Options: [DENY]
< X-XSS-Protection: [1 ; mode=block]
< Referrer-Policy: [no-referrer]

0 bytes of content (unknown content-type).


java.lang.AssertionError: Status expected:<200 OK> but was:<401 UNAUTHORIZED>
Expected :200 OK
Actual   :401 UNAUTHORIZED
<Click to see difference>

I have set logging level to TRACE, but there is no log to check our pathmatcher rules. It seems it does not check the pathmatchers in the authorizeRequest.

I have set disabled to crsf, but crsf filter still participate in the security check progress, it also tired to match the /logout url.


Solution

  • It seems the WebFluxTest annotated tests did not scan my custom SecurityConfig( which is a simple config NOT extends the XXXSecurityConfigurerAdapater) in the project, but use the Spring Boot built-in default security config instead.

    Currently I have to add my SecurityConfig explicitly to enable it in the tests.

    class TestsClass{
    
       @TestConfiguration
       @Import(SecuirtyConfig::class)
       class TestConfig
    
       //test methods
    }
    

    I am not sure it is the original design purpose or other reason.