Testing JWT and OAuth2 Endpoints

Resource Server Tests with jwt()

You do not need Keycloak running. jwt() creates an authenticated JwtAuthenticationToken directly; only the JWT decoder bean must exist, so mock it:

@WebMvcTest(OrderController.class)
@Import({ResourceServerConfig.class, JwtAuthConfig.class})
class OrderControllerJwtTest {

    @Autowired MockMvc mvc;
    @MockitoBean OrderService orderService;
    @MockitoBean JwtDecoder jwtDecoder;              // no call to the issuer during tests

    @Test
    void noTokenGives401() throws Exception {
        mvc.perform(get("/api/orders"))
           .andExpect(status().isUnauthorized())
           .andExpect(header().string("WWW-Authenticate", "Bearer"));
    }

    @Test
    void readScopeGives200() throws Exception {
        when(orderService.findByCustomer("asha")).thenReturn(List.of());
        mvc.perform(get("/api/orders").with(jwt()
                .jwt(j -> j.subject("asha").claim("scope", "orders.read"))
                .authorities(new SimpleGrantedAuthority("SCOPE_orders.read"))))
           .andExpect(status().isOk());
    }

    @Test
    void readScopeCannotPlaceOrders() throws Exception {
        mvc.perform(post("/api/orders").contentType(MediaType.APPLICATION_JSON).content("{}")
                .with(jwt().authorities(new SimpleGrantedAuthority("SCOPE_orders.read"))))
           .andExpect(status().isForbidden());
    }
}

Note: When you pass .authorities(...) explicitly, your JwtAuthenticationConverter is skipped. To test the converter too (Keycloak realm_access mapping), use .jwt(j -> j.claim("realm_access", Map.of("roles", List.of("admin")))).authorities(converterBean) — the overload that takes a Converter<Jwt, Collection<GrantedAuthority>>.

OAuth2 Login and Opaque Tokens

// a user who logged in with Google (oauth2Login)
mvc.perform(get("/api/me").with(oidcLogin()
        .idToken(token -> token.claim("email", "[email protected]").claim("name", "Asha Verma"))
        .authorities(new SimpleGrantedAuthority("ROLE_CUSTOMER"))))
   .andExpect(status().isOk())
   .andExpect(jsonPath("$.email").value("[email protected]"));

// an opaque-token resource server
mvc.perform(get("/api/orders").with(opaqueToken()
        .attributes(a -> a.put("sub", "asha"))
        .authorities(new SimpleGrantedAuthority("SCOPE_orders.read"))))
   .andExpect(status().isOk());

Running the Tests

Maven

./mvnw -q test -Dtest="*SecurityTest,*JwtTest"

Example output

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running in.shopease.order.OrderControllerJwtTest
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.912 s
[INFO] Running in.shopease.product.ProductControllerSecurityTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.284 s
[INFO] Running in.shopease.order.OrderServiceSecurityTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.406 s
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

Tip: For a full end-to-end test with real tokens, start Keycloak with Testcontainers (com.github.dasniko:testcontainers-keycloak), import a test realm JSON, fetch a token and call the API with TestRestTemplate or RestClient.