Create and Verify PASETO Tokens in Java
![Create and Verify PASETO Tokens in Java](/assets-jekyll/blog/paseto-security-tokens-java/paseto-social-eebffbc7050411ef539b334aed883bac20e1b4b407be2c5df987e7327cf82e71.png)
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.
Okta Developer Blog Comment Policy
We welcome relevant and respectful comments. Off-topic comments may be removed.