Search code examples
spring-bootspring-webfluxspring-security-oauth2

How to disable oauth2 client registration in @WebFluxTest?


Having configured oauth2:

security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: ${keycloak.server-url}/realms/${keycloak.realm}
      client:
        provider:
          keycloak:
            issuer-uri: ${keycloak.server-url}/realms/${keycloak.realm}
            user-name-attribute: email
        registration:
          keycloak:
            client-id: ${keycloak.client-id}
            client-secret: ${keycloak.client-secret}
            scope: openid

I wonder how to run a @WebFluxTest for a controller without having to run an actual keycloak server. The following test runs fine, when I have the local keycloak server running:

@WebFluxTest(TaskController::class)
class TaskControllerTest {

  @Autowired
  private lateinit var client: WebTestClient

  @MockkBean
  private lateinit var taskService: TaskServiceImpl

  @Test
  @WithMockUser(roles = ["user"])
  fun getByIds() {
    val ids = setOf<Long>(1, 2)
    every { taskService.getByIds(ids, any()) } returns flowOf(
      TaskWithRating(
        Task(
          id = 1,
          title = "title",
          description = "desc"
        ),
        Rating(0, 0),
      )
    )
    client
      .get()
      .uri { uriBuilder ->
        uriBuilder
          .path("/tasks/byIds")
          .queryParam("ids", ids)
          .build()
      }
      .exchange()
      .expectStatus().isOk
      .expectBody()
      .jsonPath("$.length()").isEqualTo(1)
      .jsonPath("$.1.task.id").isEqualTo(1)
  }
}

When I stop the local keycloak server, the test fails, because spring tries to read the oauth2 configuration during startup, while creating the ApplicationContext:

Failed to load ApplicationContext
...
Factory method 'clientRegistrationRepository' threw exception; nested exception is java.lang.IllegalArgumentException: Unable to resolve Configuration with the provided Issuer of "https://keycloak.local.com/auth/realms/myrealm"
...
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://keycloak.local.com/auth/realms/myrealm/.well-known/openid-configuration": Connection refused; nested exception is java.net.ConnectException: Connection refused
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:784)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:669)
    at org.springframework.security.oauth2.client.registration.ClientRegistrations.lambda$oidc$0(ClientRegistrations.java:156)
    at org.springframework.security.oauth2.client.registration.ClientRegistrations.getBuilder(ClientRegistrations.java:209)
    ... 175 more
Caused by: java.net.ConnectException: Connection refused

The test does not need it, because the user is mocked anyway. So I think of disabling the auth2 configuration in @WebFluxTest, but I could not find out how and if this is a valid approach?

I tried this.

@WebFluxTest(
  controllers = [TaskController::class],
  excludeAutoConfiguration = [ReactiveSecurityAutoConfiguration::class],
)

and this

@WebFluxTest(
  controllers = [TaskController::class],
  excludeAutoConfiguration = [ReactiveOAuth2ClientAutoConfiguration::class],
)

But the error is the same.


Solution

  • Turns out I was on the right path. Both, the oauth2 client and resource server auto configurations must be disabled. This works:

    @WebFluxTest(
      controllers = [TaskController::class],
      excludeAutoConfiguration = [
        ReactiveOAuth2ClientAutoConfiguration::class,
        ReactiveOAuth2ResourceServerAutoConfiguration::class,
      ],
    )
    

    Note that this bypasses potentially disabled features and hence, for example, requires a csrf() mutation on the test client:

    client
      .mutateWith(SecurityMockServerConfigurers.csrf()) 
    

    An alternative to this would be to configure a test-configuration for the security setup using @Import(TestSecurityConfiguration::class).