Create and Verify PASETO Tokens in Java

PASETO is the latest trend in security token formats. Its primary goal is to reduce the problems the JSON Web Token (JWT) related specifications introduce. In this post, I’ll give you a brief introduction to PASETO tokens and then jump into an example that creates and parses tokens using in Java using JPaseto.

If you’d rather watch a video, I created a screencast too!

What is PASETO?

PASETO stands for Platform-Agnostic SEcurity TOkens. The PASETO RFC defines a format to encode a JSON object so you can securely transfer it between two parties. Sounds a lot like JWT? So much so, the tagline of the PASETO site is:

Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards.

There has been a rash of vulnerabilities in JWT parsers; this has more to do with the JWT format than the implementations. The PASETO format aims to fix this by simplifying the format and reducing cryptographic options, simplifying implementations, and leaving developers with only the correct options.

The PASETO Format

There are two types (or "purposes") of PASETO tokens: local and public. Local tokens are encrypted with a shared key, whereas public tokens are signed with a public key pair, but NOT encrypted. In other words, anyone can read a public token, and only parties with the secret key can read local tokens.

The PASETO token format has two different versions:

  • v1 is a compatibility mode, which is ideal for legacy systems and uses cryptographic primitives that are wildly available today.

  • v2 is the recommended option, which uses the latest cryptographic primitives.

When you put this all together in a string, the format looks like this:

version.purpose.payload

Or, with the optional footer:

version.purpose.payload.footer

For example, a v1.local token looks like this:

v1.local.CuizxAzVIz5bCqAjsZpXXV5mk_WWGHbVxmdF81DORwyYcMLvzoUHUmS_VKvJ1hn5zXyoMkygkEYLM2LM00uBI3G9gXC5VrZCUM-BLZo1q9IDIncAZTxYkE1NUTMz

Introducing JPaseto!

JPaseto is a Java library modeled after the JJWT project. If you need to use JWTs, I’d strongly recommend JJWT. It’s intuitive and pluggable, and it goes to great lengths to protect you against the types of vulnerabilities JWT is known for.

I’ve put together a simple example to show you how to create and parse PASETO tokens using JPaseto. Grab it on GitHub:

git clone https://github.com/oktadeveloper/okta-jpaseto-example.git
cd okta-jpaseto-example

Creating a PASETO Token

To keep things simple, I’m going to use v1 tokens in this post. Creating and parsing v2 tokens with JPaseto works the same way, but you would need an additional dependency: a native library "libsodium" See the project readme for more details.

The requirement on libsodium will be removed from the library in the future when the cryptographic primitives become available as Java implementations.

Take a look at the createToken() method in src/main/java/com/okta.example/JPasetoExample.java

Instant now = Instant.now(); (1)

String token = Pasetos.V1.LOCAL.builder() (2)
        .setSharedSecret(SHARED_SECRET) (3)
        .setIssuedAt(now) (4)
        .setExpiration(now.plus(1, ChronoUnit.HOURS)) (5)
        .setAudience("blog-post") (6)
        .setIssuer("https://developer.okta.com/blog/") (7)
        .claim("1d20", new Random().nextInt(20) +1) (8)
        .compact(); (9)
1 Get the current date/time as an Instant
2 Set the secret key. I used Keys.secretKey() to create a random one
3 Create a "builder" for the type of token you are creating (v1.local)
4 Set the iat claim to the current time
5 Set the exp claim to one hour in the future
6 Set the Audience (aud) reserved claim value
7 Set the Issuer (iss) reserved claim value
8 Set a custom claim 1d20 to a random number between 1 and 20
9 Serialize the token into a string

Parsing a PASETO Token

Parsing tokens is just as easy. Let’s dig into the parseToken() method:

PasetoParser parser = Pasetos.parserBuilder() (1)
        .setSharedSecret(SHARED_SECRET) (2)
        .build(); (3)

Paseto result = parser.parse(token); (4)
1 Create an instance of PasetoParserBuilder
2 Use the same SHARED_SECRET that was used to create the token
3 Build the PasetoParser
4 Finally, call the parse method
You should reuse the instance of PasetoParser for most use cases.

To access the claims values inside the token, call the getClaims() method (or getFooter() to retrieve values stored in the optional token footer).

String audience = result.getClaims().getAudience(); (1)
log("Audience: "+ audience);

int rolledValue = result.getClaims().get("1d20", Integer.class); (2)
log("1d20 rolled: " + rolledValue);
1 Get the value for the Audience reserved claim
2 Get the value for the custom claim 1d20 as an Integer

Require Claims in the PASETO Token

JPaseto validates the "expiration" and "not before" attributes (or "claims") automatically for you. You can also validate other claims inside the token; for example, you may need to assert the "aud" (audience) claim has a specific value. See what this looks like in this example:

PasetoParser parser = Pasetos.parserBuilder()
        .setSharedSecret(SHARED_SECRET)
        .requireAudience("blog-post")
        .build();

Attempting to parse a PASETO token that does not meet these requirements will throw a PasetoException, and you should NOT trust that token.

Learn more about PASETO and Java Security

This post has given you an introduction to the PASETO format and showed you how easy and intuitive the JPaseto library is to use. If you want to learn more about security tokens in Java, check out the posts below!

For more posts like this one, follow @oktadev on Twitter, follow us on LinkedIn, or subscribe to our YouTube channel.

Brian Demers is a Developer Advocate at Okta and a PMC member for the Apache Shiro project. He spends much of his day contributing to OSS projects in the form of writing code, tutorials, blogs, and answering questions. Along with typical software development, Brian also has a passion for fast builds and automation. Away from the keyboard, Brian is a beekeeper and can likely be found playing board games. You can find him on Twitter at @briandemers.