Mock JWT Decoding with JwtDecoder in JUnit Tests
JSON web tokens (JWTs) are widely used for microservices and Spring security authentication. When testing Spring applications, mocking JWT decoding is often necessary. Let us delve into understanding how to test using Junit and mock JWT decoding with JwtDecoder.
1. Introduction to JWT and JwtDecoder
1.1 What is JWT?
JWT (JSON Web Token) is a compact, URL-safe token format used for securely transmitting information between parties. It is widely used for authentication and authorization in web applications. The JWT token follows the following structure:
-- Structure of a JWT Token Header.Payload.Signature -- Sample of a JWT token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTY5MjM4Mjg4MH0.Df5ZyV8T1j1WQQ4u4xNhPvDTrMQ1rP_7M5kg5lPaP_U
1.1.1 Understanding the JWT Token
In the JWT token, each part of the token is separated by a dot (.):
- Header: Contains metadata about the token, including the signing algorithm and token type. It includes:
alg: The signing algorithm used (e.g., HS256).typ: The type of token (e.g., JWT).
{"alg":"HS256","typ":"JWT"} - Payload: Holds the claims, which are pieces of information about the user or token. It includes:
sub: Subject (e.g., the user identifier).exp: Expiration time (e.g., Unix timestamp).
{"sub":"user1","exp":1692382880} - Signature: Ensures the token’s integrity and authenticity using a secret key or public-private key pair. It is generated by:
- Encoding the header and payload.
- Signing the encoded data using a secret key or private key.
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret-key )
1.2 What is JwtDecoder?
JwtDecoder is an interface in Spring Security that decodes JWTs to extract claims.
// Code Syntax JwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(secretKey).build(); Jwt jwt = jwtDecoder.decode(token);
The code snippet demonstrates the syntax for decoding a JWT using JwtDecoder in Spring Security. The first line initializes a JwtDecoder instance using NimbusJwtDecoder, which is configured with a secret key for HMAC-based JWT validation. The method withSecretKey(secretKey) specifies the secret key used for verifying the JWT signature, and the build() method finalizes the decoder’s configuration. In the second line, the decode(token) method of JwtDecoder is called to parse and validate the given JWT token. If the token is valid, it returns a Jwt object containing the token’s claims (such as subject, expiration time, and custom attributes). If the token is invalid, expired, or tampered with, an exception is thrown. This syntax is commonly used in Spring Security’s OAuth2 resource server implementations to authenticate and authorize API requests.
1.2.1 How does JwtDecoder work?
The JwtDecoder plays a crucial role in JWT-based authentication by verifying and extracting information from tokens. It ensures that the received JWT is valid, untampered, and correctly signed before allowing access to protected resources. Without proper decoding and validation, unauthorized users could manipulate tokens and gain access to sensitive data.
The verification process follows these steps:
- It verifies the JWT’s signature to ensure it hasn’t been tampered with.
- It decodes the token to extract claims such as subject, issuer, and expiration time.
- It can work with:
- A secret key (for HMAC-signed tokens like HS256).
- A public key (for RSA-signed tokens like RS256).
- A JWK Set URI (to dynamically fetch public keys from an authorization server).
1.2.2 Advantages of JwtDecoder
Key advantages of JwtDecoder include:
- Provides secure JWT verification without manually handling cryptographic logic.
- Supports different types of JWT signing mechanisms (HMAC, RSA, EC).
- Integrates seamlessly with Spring Security’s OAuth2 Resource Server.
2. Code Example
2.1 Adding Dependencies
To use JWT in Spring Security, add dependencies in pom.xml file:
<!-- Spring Security Starter for basic authentication and authorization -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JJWT (Java JWT) library for creating and parsing JWT tokens -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.11.5</version>
</dependency>
<!-- Spring Security OAuth2 Resource Server for handling JWT authentication -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<!-- JUnit Jupiter API for writing and running unit tests -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
2.2 Mocking JwtDecoder in Junit
We use Mockito to mock the JwtDecoder behavior in JUnit tests.
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtException;
import java.time.Instant;
public class JwtDecoderTest {
@Test
void testValidJwtDecoder() {
// Mock JwtDecoder
JwtDecoder jwtDecoder = mock(JwtDecoder.class);
// Create a mock valid JWT token
Jwt validJwt = Jwt.withTokenValue("valid-token")
.header("alg", "HS256")
.claim("sub", "test-user")
.claim("role", "ADMIN")
.issuedAt(Instant.now())
.expiresAt(Instant.now().plusSeconds(3600)) // Expires in 1 hour
.build();
// Define behavior for valid token
when(jwtDecoder.decode("valid-token")).thenReturn(validJwt);
// Test JWT decoding
Jwt decodedJwt = jwtDecoder.decode("valid-token");
// Assertions for valid JWT
assertNotNull(decodedJwt);
assertEquals("test-user", decodedJwt.getClaim("sub"));
assertEquals("ADMIN", decodedJwt.getClaim("role"));
}
@Test
void testExpiredJwtDecoder() {
// Mock JwtDecoder
JwtDecoder jwtDecoder = mock(JwtDecoder.class);
// Create a mock expired JWT token
Jwt expiredJwt = Jwt.withTokenValue("expired-token")
.header("alg", "HS256")
.claim("sub", "expired-user")
.claim("role", "USER")
.issuedAt(Instant.now().minusSeconds(7200)) // Issued 2 hours ago
.expiresAt(Instant.now().minusSeconds(3600)) // Expired 1 hour ago
.build();
// Define behavior for expired token
when(jwtDecoder.decode("expired-token")).thenReturn(expiredJwt);
// Test JWT decoding
Jwt decodedJwt = jwtDecoder.decode("expired-token");
// Assertions for expired JWT (claims exist, but it should be considered expired)
assertNotNull(decodedJwt);
assertEquals("expired-user", decodedJwt.getClaim("sub"));
assertEquals("USER", decodedJwt.getClaim("role"));
// Additional check (if implementing real JWT validation, this should throw an exception)
assertTrue(decodedJwt.getExpiresAt().isBefore(Instant.now()), "Token should be expired");
}
@Test
void testInvalidJwtDecoder() {
// Mock JwtDecoder
JwtDecoder jwtDecoder = mock(JwtDecoder.class);
// Define behavior for invalid token (simulate decoding failure)
when(jwtDecoder.decode("invalid-token")).thenThrow(new JwtException("Invalid JWT token"));
// Test invalid JWT decoding
Exception exception = assertThrows(JwtException.class, () -> jwtDecoder.decode("invalid-token"));
// Assertion for invalid JWT exception message
assertEquals("Invalid JWT token", exception.getMessage());
}
}
2.2.1 Code Explanation and Output
The JwtDecoderTest class demonstrates unit testing of JWT decoding using a mocked JwtDecoder in Java with JUnit. It includes three test cases to handle different JWT scenarios: valid, expired, and invalid tokens. In the first test, testValidJwtDecoder(), a valid JWT token is created with claims such as sub (subject) and role (ADMIN), along with an expiration time set to one hour ahead. The mock JwtDecoder is configured to return this token when decoding “valid-token”, and assertions verify that the correct claims are extracted. The second test, testExpiredJwtDecoder(), generates a JWT that expired one hour ago, verifying that the claims still exist but the expiration check confirms its invalidity. The final test, testInvalidJwtDecoder(), simulates an invalid JWT scenario by making the mock JwtDecoder throw a JwtException when attempting to decode “invalid-token”, ensuring that the application correctly handles decoding failures. This approach ensures robust JWT validation while leveraging Mockito for efficient unit testing.
Once the test runs are successfully executed, the output will be:
BUILD SUCCESSFUL in 1s 3 tests passed
If an assertion fails, JUnit will generate an error message like:
org.opentest4j.AssertionFailedError: Expected :test-user Actual :wrong-user
3. Conclusion
Mocking JWT decoding with JwtDecoder is crucial for writing secure and efficient tests without relying on external authentication services. Using Mockito, we can simulate JWT validation, extract claims, and test authentication logic in Spring Security applications effectively.




