JWTs could be very useful in RESTful Web Services — not only for stateless authentication, but for all the purposes that require tokens — e.g. email verification and forgot-password. In this post, we'll discuss why and how to use the Nimbus JOSE + JWT library for creating and parsing JWT (JWE) tokens.
For code examples, we’ll refer to Spring Lemon. If you haven’t heard of Spring Lemon, you should give a look. It’s a library encapsulating the sophisticated non-functional code and configuration that’s needed when developing real-world RESTful web services using the Spring framework and Spring Boot.
JWT in a nutshell
There’s already a lot of material available on JWT. So, instead of repeating those here, let's just summarize it as plainly as possible.
A JWT is a url-safe token, with some data embedded in it. The data would be signed/encrypted, so that malicious guys can’t alter it (or create another token with altered data). There are two kinds of JWTs — JWS and JWE.
JWS tokens will have their data is signed but not encrypted. That means, the data can be parsed by anyone. For example, these can be used as authentication tokens where the front-end or any client needs to read the data.
JWE tokens instead will have the data encrypted, so that no one (except the creator or someone having the secret key) can parse it.
Why Nimbus JWT
Java has two popular open source libraries for JWT creation and parsing: JJWT and Nimbus JOSE + JWT. Between these, JJWT is simple and easy to use. In fact, most articles on the Internet that discuss how to use JWT with Spring use the JJWT library.
So, why bother using the Nimbus JWT?
For a couple of reasons. First, Nimbus JWT is comprehensive. It has many useful features that are not found in JJWT. For example, it supports creating JWEs, which are essential for creating tokens to be sent through mail (e.g. forgot-password tokens).
Secondy, Spring Security 5 itself uses Nimbus JWT — its dependencies like spring-security-oauth2-client and spring-security-oauth2-jose include nimbus-jose-jwt.
So, when using Spring Security 5, it’s natural to prefer Nimbus JWT instead of any other library.
How to use Nimbus JWT
Nimbus JWT supports multiple algorithms for signing and encrypting tokens. In this post, we’ll discuss creating/parsing JWE tokens using a shared key, which would fit into most use cases when developing stateless REST Web Services — e.g. authorization, email validation, forgot-password etc.
So, let’s see how to use it. I’ll be pasting in some code snippets as examples, but for a detailed reference, you can refer to the JwtService class of Spring Lemon.
Creating a token
A JWT will have multiple parts. One of those is the payload, which contains the actual data to carry.
Creating the payload
Each piece of the data in the payload is called a claim. For example, if you’d like to pass an email and a name, first build a
ClaimsSet with two claims:
JWTClaimsSet claims = new JWTClaimsSet.Builder() .claim("email", "[email protected]") .claim("name", "Sanjay Patel") .build();
Tip: You can use the standard JWT claims — such as subject, audience or expiration time — when possible. In fact, Nimbus classes, such as the builder above, have special methods supporting those. See the JwtService class of Spring Lemon for examples.
Ok, then create the payload as below:
Payload payload = new Payload(claims.toJSONObject());
Creating the header
Another part of a JWT would be the header, which contains the encryption algorithm and method. Here is how you could create one:
JWEHeader header = new JWEHeader(JWEAlgorithm.DIR, EncryptionMethod.A128CBC_HS256);
The above says that we are going to use direct encryption with A128CBC_HS256 algorithm. So, next step is to create an encrypter for it.
Creating the encrypter
The encrypter can be created as below:
String secretKey = “841D8A6C80CBA4FCAD32D5367C18C53B”; byte secretKey = secret.getBytes(); encrypter = new DirectEncrypter(secretKey);
secretKey above is an aes-128-cbc key, generated using an online utility.
Note that the encrypter above could be a global one; you don’t need to create an encrypter per token.
Creating the token
Now that you have the header and payload and an encrypter, next steps would be create a JWE object, encrypt it, and then serialize it to produce the desired token:
JWEObject jweObject = new JWEObject(header, payload); jweObject.encrypt(encrypter); String token = jweObject.serialize();
So, your token is ready. Give it to whoever wants!
Parsing a token
Let’s now see how to parse a token. For that, we’d first need to configure a JWT Processor.
Configuring a JWT Processor
Configuring our JWT Processor can be done as below:
ConfigurableJWTProcessor<SimpleSecurityContext> jwtProcessor = new DefaultJWTProcessor<SimpleSecurityContext>(); JWKSource<SimpleSecurityContext> jweKeySource = new ImmutableSecret<SimpleSecurityContext>(secretKey); JWEKeySelector<SimpleSecurityContext> jweKeySelector = new JWEDecryptionKeySelector<SimpleSecurityContext>(JWEAlgorithm.DIR, EncryptionMethod.A128CBC_HS256, jweKeySource); jwtProcessor.setJWEKeySelector(jweKeySelector);
secretKey above must be the same one that we used earlier.
Parsing the claims
jwtProcessor can then be used to parse the claims out of our tokens, as below:
JWTClaimsSet claims = jwtProcessor.process(token, null); String email = (String) claims.getClaim(“email”); String name = (String) claims.getClaim(“name”);
So, this is a way we can use Nimbus JWTs in our applications. It does look a little harder than JJWT, but this is non-functional stuff that you need to code once and then ripe the benefits all the time. (You of course would need to maintain it as new versions of Nimbus JWT comes out, but that's where a higher level library like Spring Lemon would take your burden off.)