jwtintegration-testinguserdetailsserviceoauth2resourceserver

Spring boot integration test with Oauth using JWT and PreAuthorized


Currently I have a security configuration using spring security with oauth2 resourceServer. Its all working fine with the JWT token this is my security filter chain looks like this:

httpSecurity.authorizeRequests()
                    .anyRequest().authenticated()
                    .and().cors()
                    .and().csrf()
                    .and().oauth2ResourceServer().jwt();

I need now to introduce this custom userDetailsService bean, in order to use preAuthorized annotation with custom roles and privileges:

@Bean
    public UserDetailsService userDetailsService(IIUserService iiUserService) {
        return new CustomUserDetailsService(new IIUserServiceFake());
    }

public class CustomUserDetailsService implements UserDetailsService {


    @Override
    public UserDetails loadUserByUsername(String username) {
        //here I need to go to another micro-service to retrive the user roles/privileges and build a UserDetails Object
    }

Lets say I have this endpoint:

    @GetMapping("/custom")
    @PreAuthorize( "hasRole('ROLE_ADMIN')")
    public String getData(){
        return "I was able to see this endpoint";
    }

And my integration test looks like this:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public class ControllerTest {

    @LocalServerPort
    private int serverPort;

    @Autowired
    private TestRestTemplate testRestTemplate;

    @Test
    public void entitlementTest() {
        String uri = UriComponentsBuilder.fromHttpUrl("http://localhost").port(serverPort).path("/custom")
                .build().toUriString();
        ResponseEntity<String> forEntity = testRestTemplate.getForEntity(uri, String.class);
        assertEquals(200, forEntity.getStatusCodeValue());
    }
}

But now I want to be able to introduce roles/privileges with custom UserDetailsService Couple questions here: 1.- How do I mock the JWT resourceServer integration so that with the security enable during test the request looks authenticated. 2.- Using that UserDetailsService custom implementation how do I tell spring which field is the one to use because in my case i have a custom field comming from the token which is not call "username", lets say its called "custom_user_name".

Bassically I want to be able to test my controller that uses JWT token authentication with PreAuthorized annotation, using SpringBootTest

I already look this but still haven figure out a clean way to do this,

Again i dont want to change how the authentication is handle(JWT) but, have a custom way to determine if a user have the roles in my DB and being able to test that using integration test.

Thanks


Solution

  • I need now to introduce this custom userDetailsService bean, in order to use preAuthorized annotation with custom roles and privileges

    What you need is reading the manual and a Converter<Jwt, AbstractAuthenticationToken> bean (like JwtAuthenticationConverter with proper configuration or an implementation of your own).

    In your tests, if you use MockMvc instead of TestRestTemplate, then you can use SecurityMockMvcRequestPostProcessors.jwt() to setup the security-context for you (without an authorization server to be up and running). You could also have a look at this lib I wrote, it provides with test annotations similar to @WithMockUser, but for OAuth2. Sample integration test taken from there:

    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    @AutoConfigureMockMvc
    class SampleApiIntegrationTest {
    
        @Autowired
        MockMvc api;
    
        @Test
        @WithAnonymousUser
        void givenRequestIsAnonymous_whenGetGreet_thenUnauthorized() throws Exception {
            api.perform(get("/greet")).andExpect(status().isUnauthorized());
        }
    
        @Test
        @WithJwt("ch4mp.json")
        void givenUserIsCh4mp_whenGetGreet_thenOk() throws Exception {
            api.perform(get("/greet")).andExpect(content().string("Hello ch4mp! You are granted with [USER_ROLES_EDITOR, ROLE_AUTHORIZED_PERSONNEL]."));
        }
    }
    

    The @WithJwt above uses the authentication converter (an implementation of Converter<Jwt, ? extends AbstractAuthenticationToken>, JwtAuthenticationConverter by default in Spring Boot) to build the Authentication in stance and populate test security context, using a JSON payload in test classpath.