My authorization server is a self-hosted keycloak instance, which I'd rather mock for testing, than access real one. Now, I've defined my security filter like this:
@Bean
public SecurityFilterChain securityFilterChain (HttpSecurity http) throws Exception{
http.csrf(AbstractHttpConfigurer::disable);
http.authorizeHttpRequests(requests -> requests
.anyRequest().hasRole("USER")
);
http.oauth2ResourceServer(oauth2 -> {
oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter));
oauth2.authenticationEntryPoint(unauthorizedHandler);
}
);
// http.addFilterAfter()
http.exceptionHandling(exception -> {
exception.accessDeniedHandler(accessDeniedHandler);
});
return http.build();
}
Now, I've made been to mock JWT authorization like this:
@Bean
public SecurityMockMvcRequestPostProcessors.JwtRequestPostProcessor jwtRequestPostProcessor() {
SecurityMockMvcRequestPostProcessors.JwtRequestPostProcessor data = jwt().jwt(builder -> {
builder.issuer("http://auth.localhost/realms/CompetitionManager");
builder.claim("azp", "competition-manager-web");
builder.claim("preferred_username", "testUser");
builder.claim("email", "test@example.org");
builder.claim("resource_access", Map.of("competition-manager-web", Map.of("roles", List.of("USER"))));
});
return data;
}
Supposedly, there are keys missing, but according to docs, this will be handled by the spring security And the test looks like this:
@SpringJUnitConfig(classes = {JwtTestConfig.class})
public class ContestControllerTest {
/* Autowiring JPA repositories & interfaces */
@Autowired
private SecurityMockMvcRequestPostProcessors.JwtRequestPostProcessor defaultJwt;
@Test
@Order(2)
public void getListOfContests_then200() throws Exception {
mockMvc.perform(get("/contests/list").with(defaultJwt))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$").isArray())
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[*]", containsInAnyOrder("visible test contest 1", "visible test contest 2")));
}
Just as specified in docs, however it does not go through. Token is passed correctly, that's for sure, since I get 403 not 401, and, in the other test cases, the preferred_username
is accessed & used, but it does not pass through authorization filters, and I only get short message "Access Denied" without any details.
Am I missing something in my token definition?
When using SecurityMockMvcRequestPostProcessors
, your jwtAuthConverter
bean is not used. What the post-processor does is building the JwtAuthenticationToken
by itself.
Two options:
@WhithJwt
or @WithMockJwtAuth
from spring-addons-oauth2-test. Both will use your jwtAuthConverter
if it is exposed as a @Bean
. Usage on the project Github repo or in this Baeldung articleI wrote all that (including the SecurityMockMvcRequestPostProcessors
for OAuth2 in spring-security-test
).