Reading:
Decode JWT Token and Verify in Plain Java

Decode JWT Token and Verify in Plain Java

Metamug
Decode JWT Token and Verify in Plain Java

Struture of the JWT Token

A JWT Token looks as follows.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0IiwiYXVkIjpbImFkbWluIl0sImlzcyI6Im1hc29uLm1ldGFtdWcubmV0IiwiZXhwIjoxNTc0NTEyNzY1LCJpYXQiOjE1NjY3MzY3NjUsImp0aSI6ImY3YmZlMzNmLTdiZjctNGViNC04ZTU5LTk5MTc5OWI1ZWI4YSJ9.EVcCaSqrSNVs3cWdLt-qkoqUk7rPHEOsDHS8yejwxMw

It has following three parts.

  • header
  • payload (carrying information to be used)
  • signature

JWT Header Payload and Signature

JWT may not contain the signature section, but that would not help the server verify the token. If we do not want to store the token in the database, we should keep the signature section in the token.

1. Split the JWT Token

The token received in the request must contain 3 parts we mentioned above. Let us split the parts using String split method.

//split into 3 parts with . delimiter
String[] parts = token.split("\\.");

2. Base64 Decode

All three parts are Base64 url encoded, use the Base64 class to decode.

private static String decode(String encodedString) {
    return new String(Base64.getUrlDecoder().decode(encodedString));
}

3. Parse the JSON

Covert the payload and header strings into JSONObject objects. We need to use signature as it is for verification.

JSONObject header = new JSONObject(decode(parts[0]));
JSONObject payload = new JSONObject(decode(parts[1]));
String signature = decode(parts[2]);

4. Check the Expiry Timestamp

Check if the expiry timestamp is greater than the current timestamp. The payload contains

payload.getLong("exp") > (System.currentTimeMillis() / 1000)

If the token is expired, we can avoid the verification step and discard the token. Token expiry safegaurds the server from malicious tokens or any other token alterations.

5. Verify the Signature

Regenerate the signature using the same algorithm found the header.

private String hmacSha256(String data, String secret) {
    try {

        byte[] hash = secret.getBytes(StandardCharsets.UTF_8);
        Mac sha256Hmac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(hash, "HmacSHA256");
        sha256Hmac.init(secretKey);

        byte[] signedBytes = sha256Hmac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return encode(signedBytes);
    } catch (NoSuchAlgorithmException | InvalidKeyException ex) {
        Logger.getLogger(JWebToken.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
        return null;
    }
}

Header and payload are concatenated with a dot and signed with HMAC + SHA256 algorithm using a secret key. You need to maintain a configurable secret key somewhere safer.

String headerAndPayloadHashed = hmacSha256(parts[0] + "." + parts[1]

If you notice, we have not Base64 decoded the parts before creating the hash.

Check this regenerated token is matching the signature mentioned in the token as follows.

signature.equals(headerAndPayloadHashed, secret))

If token isn't expired and signature is valid, then the token is valid.

6. Access User information from Payload

As seen above, payload contains the information required, in this example we are not considering encryption, as we are not encouraging storing Personal information into the payload.

JSONObject payload = new JSONObject(decode(parts[1]));
long userId = payload.getLong("sub");

We can keep identifiers, we can used to indentify, operations, IDs etc whatever is necessary to identify the user for the request.

Reference

I have created a simple java file JWebToken.java to decode easily.

JWT token recieved in the String format can be used to verify and extract audience and subject information as follows.

//verify and use
JWebToken incomingToken = new JWebToken(bearerToken);
if (!incomingToken.isValid()) {
    String audience = incomingToken.getAudience();
    String subject = incomingToken.getSubject();
}

You can modify this class to improve the decoder as you like.



Comments

Post a comment
Icon For Arrow-up