I try to create Json Web Token in Java with jjwt library
But I have problem when I try to extend the expiration time.
I try it by the code below.
public class Main {
public static void main(String args[]) {
byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
Date date = new Date();
long t = date.getTime();
Date expirationTime = new Date(t + 5000l); // set 5 seconds
String compact = Jwts.builder().setSubject("Joe").setExpiration(expirationTime).signWith(SignatureAlgorithm.HS256, key).compact();
System.out.println("compact : " + compact);
try {
String unpack = Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject();
System.out.println("unpackage 0 : " + unpack);
// check if the expiration work.
Thread.sleep(3000);
System.out.println("unpackage 1 : " + Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject());
//extend the expration time.
Date date1 = new Date();
long t1 = date1.getTime();
Date expirationTime1 = new Date(t1 + 5000l); //prolongation 5 seconds
Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().setExpiration(expirationTime1).getSubject();
// check if the extend expiration work.
Thread.sleep(3000);
System.out.println("unpackage 2 : " + Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject());
} catch (InterruptedException | ExpiredJwtException ex) {
System.out.println("exception : " + ex.getMessage());
Thread.currentThread().interrupt();
}
}
The result is :
compact : eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UiLCJleHAiOjE0Mjk2NjU1MjB9.oMY2mDHvNoMZqBfic41LbiKvAyi93wIfu_WgIADb9Wc unpackage 0 : Joe unpackage 1 : Joe exception : JWT expired at 2015-04-22T08:18:40+0700. Current time: 2015-04-22T08:18:42+0700
So it mean, the unpackage2 cant run, Because it was expiration.
I trying to extend the expiration time.
Because I apply the code on web application.
If user still connect with my application, He should not get token timeout.
I have found another question like mine.
The problem is with the parsing code:
Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().setExpiration(expirationTime1).getSubject();
In this line, you're modifying the JWT that is returned by the parser. In other words, the above is equivalent to this:
Jws<Claims> jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact);
jws.getBody().setExpiration(expirationTime1).getSubject();
Notice how this code modifies the JWT returned by the parser? It does not - and cannot - modify the JWT represented by the original compact
String.
Your next line of code after that tries to parse the original (unmodified) compact
String:
// check if the extend expiration work.
Thread.sleep(3000);
System.out.println("unpackage 2 : " + Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject());
But we know this won't work because changing the state of the JWT returned by the parser does not have any effect on the original compact
String.
If your user presents a JWT to your web application and you want to 'extend the life' of the token so it won't expire, you must generate a new JWT and send that JWT back to the user. The user should send the new JWT back on future requests. You keep repeating this process for as long as you want to allow the user to keep talking to your web application without having to re-login again.
I should point out that if you don't want to worry about any of this stuff, Stormpath can perform user and JWT token authentication between the browser and your app automatically for you - you don't have to build any of this yourself (disclosure: I'm Stormpath's CTO).
Finally, you might be interested to know that JJWT's test suite already validates the correct behavior in numerous places for both expired and premature token use cases:
But, you don't have to take my word for it :) Here is your code, modified so that your expiration modifications function as described:
public class Main {
public static void main(String args[]) {
byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
Date date = new Date();
long t = date.getTime();
Date expirationTime = new Date(t + 5000l); // set 5 seconds
String compact = Jwts.builder().setSubject("Joe").setExpiration(expirationTime).signWith(SignatureAlgorithm.HS256, key).compact();
System.out.println("compact : " + compact);
try {
String unpack = Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject();
System.out.println("unpackage 0 : " + unpack);
// check if the expiration work.
Thread.sleep(3000);
System.out.println("unpackage 1 : " + Jwts.parser().setSigningKey(key).parseClaimsJws(compact).getBody().getSubject());
//Create a *new* token that reflects a longer extended expiration time.
Date date1 = new Date();
long t1 = date1.getTime();
Date expirationTime1 = new Date(t1 + 5000l); //prolongation 5 seconds
String compact2 = Jwts.builder().setSubject("Joe").setExpiration(expirationTime1).signWith(SignatureAlgorithm.HS256, key).compact();
// check if the extend expiration work.
Thread.sleep(3000);
System.out.println("unpackage 2 : " + Jwts.parser().setSigningKey(key).parseClaimsJws(compact2).getBody().getSubject());
Thread.sleep(1000);
} catch (InterruptedException | ExpiredJwtException ex) {
System.out.println("exception : " + ex.getMessage());
Thread.currentThread().interrupt();
}
}
}
Notice that a 2nd new JWT (compact2
) needed to be generated to reflect the new/latest expiration time. You cannot modify a parsed JWT and expect the changes to apply to the original compact value.
In summary, use Jwts.parser()
when you need to parse a JWT string to get a nice Java object representation of the JWT. Use Jwts.builder()
when you need to create or modify a JWT to produce a new compact String representation.
I hope that helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With