← Back to Engineering

Beyond JWT: Unlocking PASETO for Secure Token Management

2024-08-146 min readMohanapraneswaran M

Beyond JWT: Unlocking PASETO for Secure Token Management

In today's web world, secure connections are essential. JWT (JSON Web Tokens) is widely used for token-based authentication to verify requests, but it has its own disadvantages. New types of token authentication methods are emerging, and one of them is PASETO (Platform-Agnostic Security Tokens). In this article, we will first explore what JWT is and the issues associated with it. Then, we will introduce PASETO, highlighting its differences from JWT. Additionally, we will discuss the different types of PASETO tokens and provide a step-by-step guide on how to generate and validate them.

What is JWT?

JSON Web Token (JWT) is a compact, URL-safe way to transfer information between two parties. It contains three parts: a header, a payload, and a signature. The header includes the token type and signing algorithm, the payload contains details about the user and token like issuer, expiry time, user id, other claims, and the signature verifies the token's authenticity. JWTs are commonly used in authentication systems, where a server generates a token after a user logs in, and the client uses this token to access protected resources. It is a base64 encoded string.

Problems In JWT

1. Widely Misused:

  • Weak Signing Algorithms: JWT offers a variety of signing algorithms, some of which are known to be vulnerable. Developers may mistakenly choose a weak algorithm, leading to security risks.

2. Signing Algorithm:

  • None Algorithm: JWT allows for using "none" as the signing algorithm, effectively disabling signature verification. This can lead to vulnerabilities where attackers can forge or tamper with JWT tokens.

What is PASETO?

PASETO (Platform-Agnostic Security Tokens) is a modern security token format designed to be safer and more straightforward than JSON Web Tokens (JWT). Developed by Scott Arciszewski, PASETO addresses security issues inherent to JWT while promoting simplicity and robustness.

PASETO structure:

v1.local.1_5gaMPojdToeX6Kc6CV751PlwowSrvMUYuVAApH5HOdEh3MSCf3fQ6xsf1...

The first section of the PASETO is the protocol version (v1). This tells you what version of the PASETO standard is being used. At the time of writing, there are two versions (v1 and v2) of the PASETO standard.

The second section of the PASETO is the purpose (local). PASETO only defines two purposes for tokens: local or public.

The third section of the PASETO defines the actual token contents, also known as the payload. It contains details about users and the token itself, such as claims, issuer, and other relevant information.

Uses for PASETO:

You can use PASETOs for two different purposes: symmetric (aka local) and asymmetric (aka public).

Local (Symmetric Encryption)

Local PASETOs are created and encrypted using a secret key, which functions like a long password. If anyone obtains the local PASETO token, they cannot extract any useful information from it without the secret key. As long as the secret key remains secure, the PASETO is safe even if shared publicly.

Use Case: You can use local PASETOs to verify one web service with another. For instance, a microservice architecture where an internal service needs to authenticate requests to another internal service can benefit from using local PASETOs.

Public (Asymmetric Encryption)

Public PASETOs use a pair of public and private keys for encryption. The private key is used to sign the token, and the public key is used to verify its authenticity.

Use Case: Public PASETOs are ideal for scenarios where you need to ensure that a token's authenticity can be verified by multiple parties without exposing the private key. For example, in a distributed system where different clients need to validate the token's authenticity.

PASETO in Quarkus

To create and use PASETO in a Quarkus project, follow these steps. This example uses Gradle as the build tool, but you can also use Maven if you prefer.

1. Create a Quarkus Project

First, create a new Quarkus project. You can do this using the Quarkus CLI or through the Quarkus website.

2. Add Dependencies

Add the following dependencies to your build.gradle file:

implementation 'dev.paseto:jpaseto-api:0.7.0'
implementation 'dev.paseto:jpaseto-impl:0.7.0'
implementation 'dev.paseto:jpaseto-jackson:0.7.0'
implementation 'dev.paseto:jpaseto-bouncy-castle:0.7.0'

3. Implement PASETO Creation

package com.grootan;

import dev.paseto.jpaseto.Pasetos;
import dev.paseto.jpaseto.lang.Keys;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import javax.crypto.SecretKey;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Random;

@Path("/token/create")
public class CreateTokenService {
    public static final SecretKey SHARED_SECRET = Keys.secretKey();

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String createToken() {
        Instant now = Instant.now();
        String token = Pasetos.V1.LOCAL.builder()
                .setSharedSecret(SHARED_SECRET)
                .setIssuedAt(now)
                .setExpiration(now.plus(1, ChronoUnit.HOURS))
                .setAudience("blog")
                .setIssuer("https://ezto.io/")
                .claim("1d20", new Random().nextInt(20) + 1)
                .compact();
        return token;
    }
}

The following claims are set on the token:

  • setSharedSecret(SHARED_SECRET): Sets the shared secret key used for encryption.
  • setIssuedAt(now): Sets the issued-at timestamp to the current time.
  • setExpiration(now.plus(1, ChronoUnit.HOURS)): Sets the expiration time to one hour from now.
  • setAudience("blog"): Sets the audience claim to "blog".
  • setIssuer("https://ezto.io/"): Sets the issuer claim to the specified URL.
  • claim("1d20", new Random().nextInt(20) + 1): Adds a custom claim, simulating a random dice roll between 1 and 20.

4. Implement PASETO Verification:

package com.grootan;

import dev.paseto.jpaseto.Paseto;
import dev.paseto.jpaseto.PasetoParser;
import dev.paseto.jpaseto.Pasetos;
import dev.paseto.jpaseto.Version;
import dev.paseto.jpaseto.lang.Keys;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import java.security.KeyPair;
import static com.grootan.CreateTokenService.SHARED_SECRET;

@Path("/token/verify")
public class VerifyTokenService {
    private static final KeyPair KEY_PAIR = Keys.keyPairFor(Version.V1);

    @POST
    @Produces(MediaType.TEXT_PLAIN)
    public String verifyToken(String token) {
        PasetoParser parser = Pasetos.parserBuilder()
                .setSharedSecret(SHARED_SECRET)
                .setPublicKey(KEY_PAIR.getPublic())
                .requireAudience("blog")
                .requireIssuer("https://ezto.io/")
                .build();
        Paseto result = parser.parse(token);
        return result.getClaims().getIssuer();
    }
}

The parser is configured with:

  • setSharedSecret(SHARED_SECRET): The shared secret key used for symmetric encryption.
  • setPublicKey(KEY_PAIR.getPublic()): The public key used for asymmetric encryption.
  • requireAudience("blog"): A requirement that the token must have the audience "blog".
  • requireIssuer("https://ezto.io/"): A requirement that the token must have the issuer "https://ezto.io/".

Limitations of PASETO

Reusability:

  • Not reusable: PASETOs are meant to be single-use tokens and do not have protections against replay attacks.

Ecosystem:

  • Not Mature: The ecosystem around PASETO is not as mature as JWT's. There are fewer implementations and integrations available.
  • Limited Documentation: Compared to JWT, there is less documentation, tutorials, and examples available.

Learning Curve:

  • New Concepts: For developers familiar with JWT, PASETO introduces new concepts and a different approach to security tokens.
  • Complexity: Understanding and correctly implementing the PASETO specification can be more complex for developers new to it.

Interoperability:

  • Compatibility: Since JWT is more widely adopted, many systems and services are built around JWT.
  • Tooling and Infrastructure: Existing tools, middleware, and infrastructure might not support PASETO.

Algorithm Support:

  • Fixed Algorithms: PASETO uses fixed algorithms for each version. While this increases security, it limits flexibility.
  • Version Constraints: PASETO versions have strict rules about which algorithms can be used.

Conclusion

Both JWT and PASETO have their own advantages and disadvantages. As new authentication methods continue to emerge, developers will have more alternatives to explore. It is important for us to stay informed about these options and carefully evaluate which method best suits our application's needs. By understanding the strengths and weaknesses of JWT and PASETO, we can make more informed decisions to enhance the security and efficiency of our applications.