1. Introduction

JWT (JSON Web Token) is a standard that defines a compact and secure way of transmitting data along with a signature between two parties. The payload within a JWT is a JSON object that asserts some claims. This payload can be easily verified and trusted by the verifier as it’s digitally signed. JWTs can be signed using either a secret key or a public/private key pair.

In this tutorial, we’ll learn how to create and decode a JWT using the Auth0 JWT Java Library.

2. Structure of a JWT

A JWT basically consists of three parts:

  • Header
  • Payload
  • Signature

Each of these sections represents a Base64-encoded string separated by dots (‘.’) as a delimiter.

2.1. JWT Header

The JWT Header typically consists of two parts: the token type, which is “JWT”, and the signing algorithm used to sign the JWT.

The Auth0 Java JWT library provides various algorithm implementations to sign a JWT like HMAC, RSA, and ECDSA.

Let’s have a look at a sample JWT Header:

{
  "alg": "HS256",
  "typ": "JWT"
}

The above header object is then Base64-encoded to form the first part of the JWT.

2.2. JWT Payload

The JWT Payload contains a set of claims. Claims are basically statements about an entity along with some additional data.

There are three types of claims:

  • Registered – These are a set of useful predefined claims that are recommended but not mandatory. These claim names are only three characters long to keep the JWT compact. Some of the registered claims include iss (issuer), exp (expiration time), and sub (subject), among others.
  • Public – These can be defined at will by those using JWTs.
  • Private – We can use these claims to create custom claims.

Let’s take a look at a sample JWT Payload:

{
  "sub": "Baeldung Details",
  "nbf": 1669463994,
  "iss": "Baeldung",
  "exp": 1669463998,
  "userId": "1234",
  "iat": 1669463993,
  "jti": "b44bd6c6-f128-4415-8458-6d8b4bc98e4a"
}

Here, we can see that the Payload contains a private claim userId denoting the logged-in user’s ID. Also, we can find some other useful restricted claims that define additional details about the JWT.

The JWT Payload is then Base64-encoded to form the second part of the JWT.

2.3. JWT Signature

Lastly, the JWT Signature is generated when we sign the encoded header and encoded payload using a signing algorithm with a secret key. The signature can then be used to verify if the data within the JWT is valid.

It’s important to note that anyone having access to a JWT can easily decode and view its content. Signed tokens can verify the integrity of claims contained within them. If tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.

Finally, combining all three parts, we get our JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJCYWVsZHVuZyBEZXRhaWxzIiwibmJmIjoxNjY5NDYzOTk0LCJpc3MiOiJCYWVsZHVuZyIsImV4cCI6MTY2OTQ2Mzk5OCwidXNlcklkIjoiMTIzNCIsImlhdCI6MTY2OTQ2Mzk5MywianRpIjoiYjQ0YmQ2YzYtZjEyOC00NDE1LTg0NTgtNmQ4YjRiYzk4ZTRhIn0.14jm1FVPXFDJCUBARDTQkUErMmUTqdt5uMTGW6hDuV0

Next, let’s look at how we can create and manage a JWT using the Auth0 Java JWT library.

3. Using Auth0

Auth0 provides an easy-to-use Java library for creating and managing JWTs.

3.1. Dependencies

To get started, we add the Auth0 Java JWT library’s Maven dependency to our project’s pom.xml file:

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.2.1</version>
</dependency>

3.2. Configure Algorithm and Verifier

We first create an instance of the Algorithm class. In this tutorial, we’ll use the HMAC256 algorithm to sign our JWT:

Algorithm algorithm = Algorithm.HMAC256("baeldung");

Here, we initialize an instance of an Algorithm with a secret key. We’ll later use this during both the creation and verification of a token.

Further, let’s initialize an instance of JWTVerifier that we’ll be using to verify the created token:

JWTVerifier verifier = JWT.require(algorithm)
  .withIssuer("Baeldung")
  .build();

To initialize the verifier, we use the JWT.require(Algorithm) method. This method returns an instance of Verification that we can then use to build an instance of JWTVerifier.

We are now ready to create our JWT.

3.3. Create a JWT

To create a JWT, we use the JWT.create() method. The method returns an instance of the JWTCreator.Builder class. We will use this Builder class to build the JWT token by signing the claims using the Algorithm instance:

String jwtToken = JWT.create()
  .withIssuer("Baeldung")
  .withSubject("Baeldung Details")
  .withClaim("userId", "1234")
  .withIssuedAt(new Date())
  .withExpiresAt(new Date(System.currentTimeMillis() + 5000L))
  .withJWTId(UUID.randomUUID()
    .toString())
  .withNotBefore(new Date(System.currentTimeMillis() + 1000L))
  .sign(algorithm);

The above snippet returns a JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJCYWVsZHVuZyBEZXRhaWxzIiwibmJmIjoxNjY5NDYzOTk0LCJpc3MiOiJCYWVsZHVuZyIsImV4cCI6MTY2OTQ2Mzk5OCwidXNlcklkIjoiMTIzNCIsImlhdCI6MTY2OTQ2Mzk5MywianRpIjoiYjQ0YmQ2YzYtZjEyOC00NDE1LTg0NTgtNmQ4YjRiYzk4ZTRhIn0.14jm1FVPXFDJCUBARDTQkUErMmUTqdt5uMTGW6hDuV0

Let’s discuss some of the JWTCreator.Builder class methods used above to set some of the claims:

  • withIssuer() – identifies the party that created the token and signed it
  • withSubject() – identifies the subject of the JWT
  • withIssuedAt() – identifies the time at which the JWT was created; we can use this to determine the age of the JWT
  • withExpiresAt() – identifies the expiration time of the JWT
  • withJWTId() – unique identifier for the JWT
  • withNotBefore() – identifies the time before which the JWT should not be accepted for processing
  • withClaim() – used to set any custom claim

3.4. Verifying a JWT

Further, to verify a JWT, we use the JWTVerifier.verify(String) method from the JWTVerifier we initialized earlier. If the JWT is valid, the method parses the JWT and returns an instance of DecodedJWT.

The DecodedJWT instance provides various convenience methods we can use to fetch the claims contained in the JWT. If the JWT is invalid, the method throws a JWTVerificationException.

Let’s decode the JWT we created earlier:

try {
    DecodedJWT decodedJWT = verifier.verify(jwtToken);
} catch (JWTVerificationException e) {
    System.out.println(e.getMessage());
}

Once we obtain an instance of the DecodedJWT instance, we can use its various getter methods to obtain the claims.

For example, to obtain the custom claims, we use the DecodedJWT.getClaim(String) method. This method returns an instance of a Claim:

Claim claim = decodedJWT.getClaim("userId");

Here, we’re fetching our custom claim userId that we set earlier while creating the JWT. We can now obtain our claim value by calling the Claim.asString() or any other available method based on the data type of the claim:

String userId = claim.asString();

The above snippet returns the String1234″ of our custom claim.

In addition to the Auth0 Java JWT library, Auth0 also provides an intuitive web-based JWT Debugger to help us decode and verify a JWT.

4. Conclusion

In this article, we looked at the structure of a JWT and how it can be used for authentication.

We then used the Auth0 Java JWT library to create and verify the integrity of a token using its signature, algorithm, and secret key.

As always, the complete code for all the examples is available over on GitHub.