Spring Boot Testing Signed JWT with a mock Authorization Server

Charles Chan
4 min readSep 14, 2023
Photo by Luca Bravo on Unsplash

So you need to configure your Spring Application to use JWT and an OAuth Resource server. You researched on the web and decided that using a simple security filter and setting a specific property spring.security.oauth2.resourceserver.jwt.jwk-set-uri will be enough to fulfill the requirement. For example:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

http.authorizeHttpRequests(...)
.oauth2ResourceServer()
.jwt()

....
}
}

However, the big question is, how do you test it? This task isn’t an everyday occurrence, and it often presents a challenge that some developers may prefer to circumvent rather than invest the effort in unraveling its complexities.

Don’t worry, I will provide comprehensive guidance, showing each step involved in this undertaking.

Spring Security Setup

In this article, I assume that you have already configured your Spring Application with Spring Security and JWT authentication as above. If you’re uncertain about the initial setup process, I will refer you to the Official Guide, which serves as an excellent starting point for those looking to begin this configuration journey.

The specific configuration scenario we will be addressing pertains to cases where your certificate is provided by an authorization server. For instance, your Spring Application may feature a configuration resembling the following, wherein the JWK set is delivered to your application via a URL:

spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: https://idp.example.com/.well-known/jwks.json

WireMock MockMvc Test Skeleton

To initiate your test, first you need to use the WireMock MockMvc Test framework. You can start off with the provided code snippet as a foundational template for this purpose. It’s important to ensure that your test initializes and executes without any issues before moving on.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = Application.class)
@AutoConfigureMockMvc
public class MyTest {

@Autowired
private MockMvc mvc;

@RegisterExtension
final static WireMockExtension wireMockServer = WireMockExtension.newInstance().
.options(wireMockConfig().dynamicPort())
.build();

@Test
public void test() {
}
}

Once you are comfortable with a minimal test skeleton, we can setup the rest of the test case. First, we override the authorization server location.

Step 1: Override Authorization Server Location

Use the following code snippet at the outset of your test case. This directive informs Spring Security to direct its attention toward the simulated Authorization Server in lieu of the genuine one. Notice the use of @DynamicPropertySource to override properties in your Spring Boot test case.

@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.security.oauth2.resourceserver.jwt.jwk-set-uri", wireMockServer::baseUrl);
}

Step 2: Generate an RSA Key Pair and mock the Authorization Server Response

Next, we need to generate the certificates. Creating an RSA Key Pair programmatically not only simplifies the process but also enhances its long-term manageability, making it much better than the alternative of acquiring keys from disparate online sources or relying on tools like OpenSSL.

The code snippets below generate a fundamental RSA Key Pair and configure the mock Authorization Server to supply a JWK (JSON Web Key) response. This ensures a seamless integration within your application’s security infrastructure. This response includes a public RSA key, which will serve as a crucial component for your specific use case.

private RSAKey rsaKey;
private static final String KEY_ID = "... some random string ...";
...

@BeforeEach
public void beforeEach() throws Exception {
// generate an RSA Key Pair
rsaKey = new RSAKeyGenerator(2048)
.keyUse(KeyUse.SIGNATURE)
.algorithm(new Algorithm("RS256"))
.keyID(KEY_ID)
.generate();

var rsaPublicJWK = rsaKey.toPublicJWK();
var jwkResponse = format("{\"keys\": [%s]}", rsaPublicJWK.toJSONString());

// return mock JWK response
wireMockServer.stubFor(get("/")
.willReturn(aResponse()
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBody(jwkResponse)));
}

Step 3: Sign the JWT

After successfully establishing the RSA Key Pair, you can apply cryptographic signing to the JWT (JSON Web Token) claims using the private key. This process essentially involves adding a digital signature to the JWT, which serves as a means of verifying the authenticity and integrity of the token’s contents when it is later used for authentication or authorization purposes.

By employing the private key for signing, you ensure that only entities possessing the corresponding public key can verify the token’s validity, enhancing the security and trustworthiness of your authentication mechanism.

private String getSignedJwt() throws Exception {
var signer = new RSASSASigner(rsaKey);
var claimsSet = new JWTClaimsSet.Builder()
.expirationTime(new Date(new Date().getTime() + 60 * 1000))
.build();
var signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256)
.keyID(rsaKey.getKeyID()).build(), claimsSet);
signedJWT.sign(signer);
return signedJWT.serialize();
}

Step 4: Perform the Actual Test

At this point, our preparations are complete, and we are ready to execute our test.

@Test
public void test() throws Exception {
mvc.perform(post("/")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", format("Bearer %s", getSignedJwt())))
.andExpect(status().isOk());
}

Our test looks simple. It basically checks that if we provide a signed JWT token (signed using the private key generated in Step 2), the call should go through. It may not seem like much but under the cover, the following crucial steps have occurred:

  1. Under the cover, Spring Security will access the JWK server (WireMock Server) we have configured in Step 1.
  2. The WireMock server will provide Spring Security with an RSA public key in JSON format as configured in Step 2
  3. Finally, Spring Security will validate the signed JWT (remember, it was signed using the private key) using the public key it retrieves from the Mock Server. If the public key and private key were generated in pair (as in our case), the JWT is verified and the test will pass.

Overall, our test is simple but it validates the signed JWT verification flow in Spring Security using an authorization server.

Conclusion

In conclusion, we’ve navigated through several critical aspects of testing the configuration of JWT (JSON Web Token) authentication in a Spring Boot environment, particularly when dealing with an authorization server. We began by emphasizing the importance of setting up an RSA Key Pair programmatically, highlighting the advantages it offers in terms of ease of maintenance and security. We provided code snippets to guide you through this key generation process and illustrated how to instruct the mock Authorization Server to deliver a JWK (JSON Web Key) response, complete with a public RSA key.

--

--

Charles Chan

A seasoned consultant specialized in Software Development and Architecture. Charles also loves to tavel, follow him on https://www.gorestrepeat.com/