92

I am working on a web application developed using Java and AngularJS and chose to implement token authentication and authorization. For the exercise purpose, I've come to the point where I send the credentials to the server, generate a random token store it and send it back to the client. At every request to the server I'm attaching the token in the header and it works perfectly. For the authentication point of view is perfect and wouldn't need more.

However, I now want to keep track of the user type (admin, regular user...), as well as it's id, or any other unique field; as I understood I have to encrypt that in the token that I'm sending back to the client during the log in action. Is that correct?

Is there any JWT library that you used and can generate, encrypt and decrypt such tokens? A link to the library's API and Maven dependency would be much appreciated.

Thanks

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Marius Manastireanu
  • 2,461
  • 5
  • 19
  • 29
  • 1
    If the information that you want to store in the token is not sensitive you don't necessarily need to encrypt the token. Is a user id and permissions something secret? Probably no. What you need to ensure is that only you can create a valid token. The jwt approach is to have the token digitally signed with Hmac and a secret signature key to ensure that you will be able to verify its integrity and origin. My answer below provides a library and example. – Marquez Sep 02 '14 at 19:22
  • 1
    Hi.. I'm trying to implement this JWT library too and I did on the server side ( Java ) but how can I decode in my front end side ( javascript)? Which library did you use to decode it on the angularjs part? – Thiago Miranda de Oliveira Sep 23 '14 at 17:34
  • Thiago, I didn't. The flow was as follows: User logs in -> data sent to server -> token created -> sent back to client. Whenever a request is done to the server, the token was appended in the header (I've implemented an interceptor for this). The validation was done on the server and correct response was sent back (if it was authorized or not). – Marius Manastireanu Sep 24 '14 at 13:32
  • 2
    A useful page when dealing with JWT: http://jwt.io/ – Vilmantas Baranauskas Feb 09 '15 at 07:56
  • @MariusManastireanu The token that you are sending from angular is the same token you received from server?? I am working on the same part...please help – kittu Jan 01 '16 at 18:38
  • Also wondering, Why are not decoding the token at front end angular when you receive it from server. Is it not required to decode? – kittu Jan 01 '16 at 19:23

9 Answers9

65

JJWT aims to be the easiest to use and understand JWT library for the JVM and Android:

https://github.com/jwtk/jjwt

Les Hazlewood
  • 18,480
  • 13
  • 68
  • 76
  • 5
    Simple, easy and clean, and worked immediately. I went for the Google JsonToken first and switched here after messing with unresolved dependencies and pages of code to assemble a simple JWT. – Oliver Hausler Feb 12 '15 at 00:17
  • 1
    How can I prevent token theft? – JHS Mar 29 '15 at 21:46
  • How can I handle Token Timeout like in [nodejs-jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) – vanduc1102 Apr 16 '15 at 08:23
  • I'm able to use jjwt in a simple test program. But when I try using it in my dropwizard application I get the following error at startup: java.lang.NoSuchFieldError: WRITE_DURATIONS_AS_TIMESTAMPS. Thoughts? – Ya. May 22 '15 at 16:22
  • "How can I prevent token theft" - I can't think of anything but SSL – Ya. May 22 '15 at 16:25
  • 1
    "How can I handle Token Timeout" - Per jwt spec you need to set the "exp" claim. Jjwt provides a convenient "setExpiration" method for this. – Ya. May 22 '15 at 16:28
  • @Ya. - Your problem there was a Jackson version mismatch. jjwt depends on Jackson and you likely have an older version somewhere in your project that is causing a conflict. – Martin Snyder Sep 10 '15 at 17:53
  • How can I verify signature of token received from client? – kittu Feb 27 '16 at 10:37
  • @Satyadev if it does not throw an exception during parsing, the signature is verified. – Les Hazlewood Feb 29 '16 at 17:36
  • JJWT doesn't support JWE (Encryption for JWT) (worth to know in case someone need it) – SathOkh Jul 28 '16 at 19:18
  • @SathOkh it's in the works and almost complete. It will be in the JJWT 0.7 release. Branch here: https://github.com/jwtk/jjwt/tree/jwe Also, the readme will always tell you what is or is not supported. – Les Hazlewood Jul 28 '16 at 21:33
  • I tried it. I used key "secret123" to sing and "secret124" to veryfiy, and got it verified. – Patryk Dobrowolski Mar 15 '18 at 13:58
  • @PatrykDobrowolski that's because you were using invalid insecure Base64 keys. This is explained here: https://github.com/jwtk/jjwt/issues/211#issuecomment-283076269 – Les Hazlewood Jan 07 '19 at 23:10
  • 3
    A warning about this. There are lots of people having problems with runtime dependency resolution. I still don't understand why the author couldn't have just made the library like any other normal library out there. I ended up using something else because the author kept insisting that the configuration was wrong. But I had set it up exactly like he did on GitHub. Delayed my project by a couple of days or so. – TheRealChx101 Jul 12 '19 at 15:01
  • @TheRealChx101 there aren't 'lots of people'. There are literally thousands of JJWT users and we've received only about 3 or 4 reports. I'm 100% happy to fix whatever might be wrong if you can point to a sample project that demonstrates the issue. We just can't repeat it. – Les Hazlewood Jul 13 '19 at 18:32
  • 3
    @LesHazlewood I'm using https://github.com/auth0/java-jwt and it works even better. No weird esoteric dungeon witchcraft. – TheRealChx101 Jul 13 '19 at 19:06
  • @TheRealChx101 The Auth0 JWT library has a history of security holes. Maybe they're all resolved now, maybe not, but I and many others don't trust it because of its security history. Have fun with that! – Les Hazlewood Jul 15 '19 at 15:26
  • 1
    @LesHazlewood https://jwt.io/. I'm sure they'd have issued a warning there seeing as it's on the wall of fame. – TheRealChx101 Jul 16 '19 at 15:22
  • sorry, found it to be far too complex, I just need to read a JWT token on my android app, Auth0 offered me a far simpler API for reading the token. – Eman Nov 12 '21 at 22:15
28

If anyone in the need for an answer,

I used this library: http://connect2id.com/products/nimbus-jose-jwt Maven here: http://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt/2.10.1

Marius Manastireanu
  • 2,461
  • 5
  • 19
  • 29
  • 2
    Please, make sure you use the latest version. The library is being continuously reviewed & improved and new algorithms / features are being added: http://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt As of version 3.2 all standard JWS signature algorithms as fully supported as well as all JWE encryption algs, save for PBES2 and ECDH-ES. – Vladimir Dzhuvinov Nov 14 '14 at 11:18
  • I am getting the following error: java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.decodeBase64 – Bear Jan 21 '15 at 09:32
  • The above problem is documented here https://bitbucket.org/connect2id/nimbus-jose-jwt/issue/52/library-does-not-work-on-android-b-c-of . I resolved the issue by modifying the byte[] decode() methd in com.nimbusds.jose.util.Base64Url to use the android.util.Base64.decode method. Is this the correct way? – Bear Jan 21 '15 at 10:07
  • 1
    In version 2.24 we switched from Apache Commons Codec to an internal utility to handle BASE64 encoding + decoding. With that the Android issue with the codec has been successfully resolved. See issue https://bitbucket.org/connect2id/nimbus-jose-jwt/issue/63 for more details. Plus you should get even better performance now :) – Vladimir Dzhuvinov Mar 12 '15 at 10:10
  • @MariusManastireanu Hi, I am starting with angularjs + java authentication using JWT. Could you please guide me or show a sample code in how to achieve this? – kittu Dec 27 '15 at 08:17
  • I got classdefnotfound error on Base64Url – Anton Duzenko Mar 14 '16 at 17:52
22

By referring to https://jwt.io/ you can find jwt implementations in many languages including java. Also the site provide some comparison between these implementation (the algorithms they support and ....).

For java these are mentioned libraries:

Alireza Fattahi
  • 42,517
  • 14
  • 123
  • 173
  • Thanks, it helped me to find JWT plugin for my play framework app – chabeee Jun 21 '16 at 07:43
  • 1
    how do you guys choose which one to use? What if all 4 libraries cover all my use cases? – jkerak Sep 08 '16 at 17:38
  • 6
    @jkerak Go with whatever one seems simplest to you to implement. If all the implementations seem to be greek to you, go with the one with the coolest name. This has worked out well for me. – TheFunk Oct 18 '16 at 18:33
  • @jkerak I tried the first two libraries. The jjwt lib is very confusing and took me a couple of hours to test around while still cannot make it work. The https://github.com/auth0/java-jwt is the simplest and just worked directly without any effort. Hightly recommend https://github.com/auth0/java-jwt – flame3 Dec 02 '20 at 16:29
14

This library seems to work well: https://code.google.com/p/jsontoken/ .

It depends on Google Guava. Here are the Maven artifacts:

<dependency>
    <groupId>com.googlecode.jsontoken</groupId>
    <artifactId>jsontoken</artifactId>
    <version>1.0</version>
</dependency>
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>

The library is in fact used by Google Wallet.

Here is how to create a jwt, and to verify it and deserialize it:

import java.security.InvalidKeyException;
import java.security.SignatureException;
import java.util.Calendar;
import java.util.List;

import net.oauth.jsontoken.JsonToken;
import net.oauth.jsontoken.JsonTokenParser;
import net.oauth.jsontoken.crypto.HmacSHA256Signer;
import net.oauth.jsontoken.crypto.HmacSHA256Verifier;
import net.oauth.jsontoken.crypto.SignatureAlgorithm;
import net.oauth.jsontoken.crypto.Verifier;
import net.oauth.jsontoken.discovery.VerifierProvider;
import net.oauth.jsontoken.discovery.VerifierProviders;

import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;

import com.google.common.collect.Lists;
import com.google.gson.JsonObject;


/**
 * Provides static methods for creating and verifying access tokens and such. 
 * @author davidm
 *
 */
public class AuthHelper {

    private static final String AUDIENCE = "NotReallyImportant";

    private static final String ISSUER = "YourCompanyOrAppNameHere";

    private static final String SIGNING_KEY = "LongAndHardToGuessValueWithSpecialCharacters@^($%*$%";

    /**
     * Creates a json web token which is a digitally signed token that contains a payload (e.g. userId to identify 
     * the user). The signing key is secret. That ensures that the token is authentic and has not been modified.
     * Using a jwt eliminates the need to store authentication session information in a database.
     * @param userId
     * @param durationDays
     * @return
     */
    public static String createJsonWebToken(String userId, Long durationDays)    {
        //Current time and signing algorithm
        Calendar cal = Calendar.getInstance();
        HmacSHA256Signer signer;
        try {
            signer = new HmacSHA256Signer(ISSUER, null, SIGNING_KEY.getBytes());
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        }

        //Configure JSON token
        JsonToken token = new net.oauth.jsontoken.JsonToken(signer);
        token.setAudience(AUDIENCE);
        token.setIssuedAt(new org.joda.time.Instant(cal.getTimeInMillis()));
        token.setExpiration(new org.joda.time.Instant(cal.getTimeInMillis() + 1000L * 60L * 60L * 24L * durationDays));

        //Configure request object, which provides information of the item
        JsonObject request = new JsonObject();
        request.addProperty("userId", userId);

        JsonObject payload = token.getPayloadAsJsonObject();
        payload.add("info", request);

        try {
            return token.serializeAndSign();
        } catch (SignatureException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Verifies a json web token's validity and extracts the user id and other information from it. 
     * @param token
     * @return
     * @throws SignatureException
     * @throws InvalidKeyException
     */
    public static TokenInfo verifyToken(String token)  
    {
        try {
            final Verifier hmacVerifier = new HmacSHA256Verifier(SIGNING_KEY.getBytes());

            VerifierProvider hmacLocator = new VerifierProvider() {

                @Override
                public List<Verifier> findVerifier(String id, String key){
                    return Lists.newArrayList(hmacVerifier);
                }
            };
            VerifierProviders locators = new VerifierProviders();
            locators.setVerifierProvider(SignatureAlgorithm.HS256, hmacLocator);
            net.oauth.jsontoken.Checker checker = new net.oauth.jsontoken.Checker(){

                @Override
                public void check(JsonObject payload) throws SignatureException {
                    // don't throw - allow anything
                }

            };
            //Ignore Audience does not mean that the Signature is ignored
            JsonTokenParser parser = new JsonTokenParser(locators,
                    checker);
            JsonToken jt;
            try {
                jt = parser.verifyAndDeserialize(token);
            } catch (SignatureException e) {
                throw new RuntimeException(e);
            }
            JsonObject payload = jt.getPayloadAsJsonObject();
            TokenInfo t = new TokenInfo();
            String issuer = payload.getAsJsonPrimitive("iss").getAsString();
            String userIdString =  payload.getAsJsonObject("info").getAsJsonPrimitive("userId").getAsString();
            if (issuer.equals(ISSUER) && !StringUtils.isBlank(userIdString))
            {
                t.setUserId(new ObjectId(userIdString));
                t.setIssued(new DateTime(payload.getAsJsonPrimitive("iat").getAsLong()));
                t.setExpires(new DateTime(payload.getAsJsonPrimitive("exp").getAsLong()));
                return t;
            }
            else
            {
                return null;
            }
        } catch (InvalidKeyException e1) {
            throw new RuntimeException(e1);
        }
    }


}

public class TokenInfo {
    private ObjectId userId;
    private DateTime issued;
    private DateTime expires;
    public ObjectId getUserId() {
        return userId;
    }
    public void setUserId(ObjectId userId) {
        this.userId = userId;
    }
    public DateTime getIssued() {
        return issued;
    }
    public void setIssued(DateTime issued) {
        this.issued = issued;
    }
    public DateTime getExpires() {
        return expires;
    }
    public void setExpires(DateTime expires) {
        this.expires = expires;
    }
}

This is based on code here: https://developers.google.com/wallet/instant-buy/about-jwts And Here: https://code.google.com/p/wallet-online-sample-java/source/browse/src/com/google/wallet/online/jwt/util/WalletOnlineService.java?r=08b3333bd7260b20846d7d96d3cf15be8a128dfa

Marquez
  • 5,891
  • 3
  • 31
  • 40
8

IETF has suggested jose libs on it's wiki: http://trac.tools.ietf.org/wg/jose/trac/wiki

I would highly recommend using them for signing. I am not a Java guy, but seems like jose4j seems like a good option. Has nice examples as well: https://bitbucket.org/b_c/jose4j/wiki/JWS%20Examples

Update: jwt.io provides a neat comparison of several jwt related libraries, and their features. A must check!

I would love to hear about what other java devs prefer.

Priyeshj
  • 1,295
  • 2
  • 17
  • 32
  • jose4j has lots of JWT support too and [good examples on using it](https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples) in addition to the JWS examples you pointed out. – Brian Campbell Mar 03 '16 at 17:03
7

I found this to be small and complete https://github.com/auth0/java-jwt

mfirry
  • 3,634
  • 1
  • 26
  • 36
4

This page keeps references to implementations in various languages, including Java, and compares features: http://kjur.github.io/jsjws/index_mat.html

Hans Z.
  • 50,496
  • 12
  • 102
  • 115
3

If you only need to parse unsigned unencrypted tokens you could use this code:

boolean parseJWT_2() {
    String authToken = getToken();
    String[] segments = authToken.split("\\.");
    String base64String = segments[1];
    int requiredLength = (int)(4 * Math.ceil(base64String.length() / 4.0));
    int nbrPaddings = requiredLength - base64String.length();

    if (nbrPaddings > 0) {
        base64String = base64String + "====".substring(0, nbrPaddings);
    }

    base64String = base64String.replace("-", "+");
    base64String = base64String.replace("_", "/");

    try {
        byte[] data = Base64.decode(base64String, Base64.DEFAULT);

        String text;
        text = new String(data, "UTF-8");
        tokenInfo = new Gson().fromJson(text, TokenInfo.class);
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }

    return true;
}
Community
  • 1
  • 1
Anton Duzenko
  • 2,366
  • 1
  • 21
  • 26
2

https://github.com/networknt/jsontoken

This is a fork of original google jsontoken

It has not been updated since Sep 11, 2012 and depends on some old packages.

What I have done:

Convert from Joda time to Java 8 time. So it requires Java 8.
Covert Json parser from Gson to Jackson as I don't want to include two Json parsers to my projects.
Remove google collections from dependency list as it is stopped long time ago.
Fix thread safe issue with Java Mac.doFinal call.

All existing unit tests passed along with some newly added test cases.

Here is a sample to generate token and verify the token. For more information, please check https://github.com/networknt/light source code for usage.

I am the author of both jsontoken and Omni-Channel Application Framework.

Steve Hu
  • 358
  • 2
  • 10