I want to test my JwtService which is for generating and handling JWT Tokens. I am not even sure, if I have to test it because not all the methods are mine but I still would like to see if the token is generated correct. But if I want to test if the generated token is correct I have to compare it to another token. The only problem is the time difference, so iat (Issued At) and exp (Expiration Time/Date) are different. That's why the test always fails. How can I test even though the iat and exp are different?
JwtService:
package de.gabriel.vertretungsplan.security.service;
import de.gabriel.vertretungsplan.models.UserDetailsImpl.UserDetailsImpl;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Service;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Service
public class JwtService {
/*
Generated with: https://www.allkeysgenerator.com/; 256-bit key
*/
private static final String SECRET_KEY = "secret_key";
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
public String generateToken(UserDetailsImpl userDetails) {
return generateToken(new HashMap<>(), userDetails);
}
public String generateToken(
Map<String, Object> extraClaims,
UserDetailsImpl userDetails
) {
return Jwts.builder()
.setClaims(extraClaims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + (1000 * 60 * 60 * 24 * 7))) // 7 days
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public boolean isTokenValid(String token, UserDetailsImpl userDetails) {
final String username = extractUsername(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
private Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
private Claims extractAllClaims(String token) {
return Jwts
.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
private Key getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
return Keys.hmacShaKeyFor(keyBytes);
}
}
This is how I want to test it:
@Test
public void generateToken_shouldGenerateToken() {
Verwaltung verwaltung = new Verwaltung(
"user",
"user@gmail.com",
"user",
Rolle.getPrefixedRolle(Rolle.USER)
);
String generatedToken = jwtService.generateToken(new UserDetailsImpl(verwaltung));
assertEquals(token, generatedToken);
}
The expected token
was generated on jwt.io with the same secret key and everything as reference/ expected token. Does anyone have an idea how to test it even though there is a time difference?
You could take a different approach and verify the contents of the JWT instead of comparing it with a pre-generated token (which contains a timestamp (iat and exp fields), so it will always be different from a pre-generated token):
@Test
public void generateToken_shouldGenerateToken() {
Verwaltung verwaltung = new Verwaltung(
"user",
"user@gmail.com",
"user",
Rolle.getPrefixedRolle(Rolle.USER)
);
String generatedToken = jwtService.generateToken(new UserDetailsImpl(verwaltung));
// parse generated token
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(SECRET_KEY))
.parseClaimsJws(generatedToken)
.getBody();
assertEquals(verwaltung.getUsername(), claims.getSubject());
// check if the token is not expired
assertFalse(claims.getExpiration().before(new Date()));
}