Integration testing a Spring RESTful Web Service secured with OAuth2

-

The last system that I worked on had a component that offered a RESTful Web Service. At some point, we added Spring Security (oAuth2) to the REST endpoints and the integration tests of the REST interface stopped working. Like many others, we like having automated tests. Integration tests help us enforce the REST interface stability. And in turn, a stable REST interface gives us confidence to do refactoring. And that makes us happy. 🙂

In this article, I will explain how to write integration tests for an application that uses Spring Security with OAuth2. The only prerequisite is basic Spring knowledge. You can download the working sample code here.

Getting the integration tests to work

We weren’t the first ones to add security via OAuth2 to a Spring RESTful Web Aervice. Finding an example on how to do this on the web would be easy. Right?
Wrong…
There are some interesting resources that treat integration tests and Spring Security, but none of them describe what happens in the case of OAuth2:

  • a couple of stackoverflow questions touch on the topic of OAuth2, Spring and testing: [1][2][3]. The instructions in the last link lead me to the solution that I’ll describe a bit later.
  • Jhipster also comes close: it allows the generation of a Spring project secured with OAuth2 (with integration tests). But their security implementation differs from ours and I did not feel like reworking our security model. I recommend checking their solution out, if you’re starting from scratch.
  • the sample applications provided together with the Spring OAuth2 implementation are a bit too complex

After reading all that information, I tried out a couple of the proposed solutions. It took a while to get to the one that worked and I didn’t really understand why it worked. I decided to research it more and then come back to share my findings.

Putting everything together

Spring Security is BIG. The great news is that you don’t have to understand how the  model works to get the integration tests to work with OAuth2. The key to making the tests run is the RequestPostProcessor. We can use this interface to alter the MockHttpServletRequest that is sent to the service. For example, you could add HTTP headers, parameters or cookies.
The spring-security-test package already has a couple of RequestPostProcessors in SecurityMockMvcRequestPostProcessors. Unfortunately, we cannot use any of them to inject the OAuth2 header in the request. We need to write our own implementation of RequestPostProcessor.

Tim te Beek wrote an example, accompanied by integration tests, for an OAuth2 RequestPostProcessor. The part that does the magic is the OAuthHelper. The most important change that I did to his example was to add specific user roles to the Authentication object.

@Component
public class OAuthHelper {
 
    @Autowired
    AuthorizationServerTokenServices tokenservice;
 
    public RequestPostProcessor addBearerToken(final String username, String... authorities) {
        return mockRequest -> {
            // Create OAuth2 token
            OAuth2Request oauth2Request = new OAuth2Request(null, ClientService.OAUTH_CLIENT_ID, null, true, null, null, null, null, null);
            Authentication userauth = new TestingAuthenticationToken(username, null, authorities);
            OAuth2Authentication oauth2auth = new OAuth2Authentication(oauth2Request, userauth);
            OAuth2AccessToken token = tokenservice.createAccessToken(oauth2auth);
 
            // Set Authorization header to use Bearer
            mockRequest.addHeader("Authorization", "Bearer " + token.getValue());
            return mockRequest;
        };
    }
}

I already said that, to make the tests run, we needed to use a RequestPostProcessor (line 17).
This will add the proper OAuth2 HTTP header to the MockHttpServletRequest(line 16).
To get a valid OAuth2 access token, we need to get a reference to the AuthorizationServerTokenServices (line 4, 5). This bean belongs to the Spring implementation of OAuth2 and defines the operations that are necessary to manage OAuth 2.0 access tokens (create, refresh, get).
From tokenservice.createAccessToken(oauth2auth), to OAuth2Request, the code just creates the objects required by the various constructors, the javadocs should be enough to get you through that.

Once the OAuthHelper is in place, writing an Spring integration test for a secured REST endpoint is easy:

@Test
public void testHelloAgainAuthenticated() throws Exception {
  RequestPostProcessor bearerToken = authHelper.addBearerToken("test", "ROLE_USER");
  ResultActions resultActions = restMvc.perform(post("/hello-again").with(bearerToken)).andDo(print());
 
  resultActions
    .andExpect(status().isOk())
    .andExpect(content().string("hello again"));
}

Conclusion

That’s it. Integration tests are running again and I’m able to refactor without fear of breaking something in production. Two related questions remain:

  • The Spring security docs mention that annotations can be used in tests: @WithMockUser, @WithUserDetails, @WithSecurityContext. I wonder why they did not work for me
  • I’ve seen some threads on stackoverflow mentioning something about OAuth2 and a stateless ResourceServerSecurityConfigurer. How does that work?

The code snippets used in the article are part of the complete application that is available on github.
And if you have any suggestions, questions or answers (to the two remaining questions), don’t be shy, use the comment form.