So are there any worthwhile alternatives to JWTs?
JSON Web Tokens (JWT) are a popular way to create URL-safe access tokens for web applications. They are often used for stateless sessions1 and they’re part of OpenID Connect (OIDC) protocol.
Note: JWT is just one part of the JavaScript Object Signing and Encryption (JOSE) framework. It would be more accurate to talk about JOSE but JWT has come to represent the whole suite, so I’ll go with that.
Unfortunately, JWT is not a great design. It’s complex and many implementations have had serious security flaws directly connected to that complexity. For example: No Way JOSE! Javascript Object Signing and Encryption is a Bad Standard That Everyone Should Avoid
Recently I had a legitimate need for a URL-safe authenticated token. I needed to pass some information via the user’s browser from one service to another one that I control. The receiving service had to be verify that the information was coming from the original service intact.
The common wisdom on Twitter is that just slap a HMAC-SHA256 on it. That seems like a good idea. To avoid any issues with canonicalization, I’d dump my data as JSON, encode the result with base64url (no padding!), and authenticate that with HMAC-SHA256 and a secret shared by the services.
This sounds very much like a JWS (the signed variant of JWT). I’d need to implement it in two programming languages, JavaScript and Clojure. That sounded like a chore and the temptation to just a library was weighing heavy on me. But since JWTs are bad, I decided to take a look at alternatives.
I know of a couple of alternatives to JWTs:
Fernet. Encrypted tokens only. Encrypted with AES-128 in CBC mode and encoded with base64url. The choice of cryptographic algorithm seems a bit dated by now. Simple.
Branca. Encrypted tokens only. Encrypted with XChaCha20-Poly1305 and encoded with base62. Intended as a modern version of Fernet. Simple, but base62 seems like an odd choice when base64url implementations are so widely available.
PASETO. Has both signed and encrypted variants. Version 2 (the latest version) uses XChaCha20-Poly1305 with base64url encoding. More complex and e.g. has its own string array encoding. Still less complex than JWT.
I don’t know how both Branca and PASETO both ended up with XChaCha20-Poly1305. It’s not something that I’m familiar with and I don’t follow the latest developments in cryptography, but I’m going to assume that it’s a good, modern choice. At least it’s available in libsodium.
Any of these tokens standards could have solved my problem. I didn’t need encryption but I don’t mind it as long as the tokens are authenticated.
Nevertheless, I decided to go with JWTs. I took a look at the library situation of the alternative tokens and either the libraries did not exist or they looked like they hadn’t seen much use.
Working with OIDC, I’ve had to deal with JWTs for years, and by this point there are libraries that I trust despite the abysmal track record of JWT libraries in general (shoutout to Buddy for Clojure!). There are also tools such as JWT Debugger and step command-line tool. Creating a library and tooling myself would be possible, but not a worthwhile investment for an one-off library.
Looks like I’m going to be stuck with JWTs for a while.
-
Stateless meaning that there’s no server-side state. The session state lives in the token. It sounds great on paper, but in my experience you’ll end up introducing server-side state by the time you try to make logout work the way you want. For more information, see Stop using JWT for sessions, part 1 and part 2 by Sven Slootweg. ↩︎