Home > Net >  How to mock OAuth2 token introspect using Spock
How to mock OAuth2 token introspect using Spock

Time:10-26

I have a spring-boot Webflux application and I am writing some tests using Spock and Groovy.

My Controllers are secured with OAuth opaque token which I need to mock a response from introspection. My test properties are:

spring.security.oauth2.resourceserver.opaquetoken.client-id=fake_client
spring.security.oauth2.resourceserver.opaquetoken.client-secret=fake_secret
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:8089/api/v1/oauth/token/introspect

My test uses WebClient as below:

webClient.post()
    .uri(URL.toString()))
    .accept(MediaType.APPLICATION_JSON)
    .headers(http -> http.setBearerAuth("bearer_token"))
    .exchange()
    .expectStatus()
    .is2xxSuccessful()

CodePudding user response:

I found the solution.

You have to configure WireMockServer and then stub the response. Working solution below:

@ContextConfiguration
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class SomeTest extends Specification {

  private static final int PORT = 8089;

  private WireMockServer wireMockServer;

  def setup() {
    wireMockServer = new WireMockServer(options().port(PORT))
    wireMockServer.start()
    WireMock.configureFor("localhost", wireMockServer.port())

  def stubIntrospection() {
    stubFor(post("/api/v1/oauth/token/introspect")
        .willReturn(aResponse()
            .withStatus(200)
            .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
            .withBodyFile("path-to-my-file.json")))
  }

  def "my test case" () {

    stubIntrospection()

    //my test here    
  }

  }

CodePudding user response:

There is no difference between Spock and JUnit when mocking spring security-context. You can provide with mocked Authentication for both unit (@WebFluxTest) and integration (@SpringBootTest) with either:

  • org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOpaqueToken mutator for WebTestClient
  • @WithMockBearerTokenAuthentication annotation.

Mocked BearerTokenAuthentication, Spring's auth implementation for token introspection (unless you explicitly changed it in your conf), is put in test security-context directly by annotation or mutator: authorization header is not introspected (it is completely ignored and can be omitted).

Sample (with JUnit, just adapt to your test framework) in this project:

@Test
void securedRouteWithAuthorizedPersonnelViaMutatorIsOk() throws Exception {
    api
        .mutateWith(mockOpaqueToken()
            .attributes(attributes -> attributes.put(StandardClaimNames.PREFERRED_USERNAME, "Ch4mpy"))
            .authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL")))
        .get().uri("https://localhost/secured-route")
        .exchange()
        .expectStatus().isOk();
}

@Test
@WithMockBearerTokenAuthentication(
    authorities = "ROLE_AUTHORIZED_PERSONNEL",
    attributes = @OpenIdClaims(preferredUsername = "Ch4mpy"))
void securedRouteWithAuthorizedPersonnelViaAnnotationIsOk() throws Exception {
    api
        .get().uri("https://localhost/secured-route")
        .exchange()
        .expectStatus().isOk();
}

When testing other secured @Component type than @Controller (like @Service or @Repository), only test annotation is usable as there is no HTTP request:

@Import({ SecurityConfig.class, SecretRepo.class })
class SecretRepoTest {
    // auto-wire tested component
    @Autowired
    SecretRepo secretRepo;

    @Test
    void whenNotAuthenticatedThenThrows() {
        // call tested components methods directly (do not use MockMvc nor
        // WebTestClient)
        assertThrows(Exception.class, () -> secretRepo.findSecretByUsername("ch4mpy").block());
    }

    @Test
    @WithMockBearerTokenAuthentication(attributes = @OpenIdClaims(preferredUsername = "Tonton Pirate"))
    void whenAuthenticatedAsSomeoneElseThenThrows() {
        assertThrows(Exception.class, () -> secretRepo.findSecretByUsername("ch4mpy").block());
    }

    @Test
    @WithMockBearerTokenAuthentication(attributes = @OpenIdClaims(preferredUsername = "ch4mpy"))
    void whenAuthenticatedWithSameUsernameThenReturns() {
        assertEquals("Don't ever tell it", secretRepo.findSecretByUsername("ch4mpy").block());
    }
}

Annotation is available from

<dependency>
    <groupId>com.c4-soft.springaddons</groupId>
    <artifactId>spring-addons-oauth2-test</artifactId>
    <scope>test</scope>
</dependency>

Which is a transient dependency of

<dependency>
    <groupId>com.c4-soft.springaddons</groupId>
    <artifactId>spring-addons-webflux-introspecting-test</artifactId>
    <scope>test</scope>
</dependency>

I used the later in sample to also have WebTestClientSupport, but first is enough for just test annotations.

  • Related