Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend expiration time java json web token?

Tags:

java

token

jwt

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.

like image 958
vanduc1102 Avatar asked Apr 20 '15 09:04

vanduc1102


1 Answers

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:

  • https://github.com/jwtk/jjwt/blob/0.4/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy#L163-L189
  • https://github.com/jwtk/jjwt/blob/0.4/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy#L307-L335
  • https://github.com/jwtk/jjwt/blob/0.4/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy#L421-L454

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!

like image 157
Les Hazlewood Avatar answered Sep 27 '22 16:09

Les Hazlewood