Intro to PASETO with Java
Tags: security, jwt, paseto, quarkus
Published: 2024-07-07 10:59:46

In this article, we will first see what JWT (JSON Web Tokens) is and the issues associated with it. Then, we will introduce PASETO (Platform-Agnostic Security Tokens), highlighting its differences from JWT. We will also discuss the different types of PASETO tokens and provide a step-by-step guide on how to generate and validate PASETO tokens.

## 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 claims about the user and additional data, 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 in the Token Header:

- Algorithm Confusion: The inclusion of the signing algorithm in the token header can lead to security issues. Attackers can modify the alg value in the header, potentially changing the signature verification method. If the implementation does not strictly enforce the expected algorithm, this can result in token forgery, allowing unauthorized access.

## 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_zSprLvcDqUxXZIjNbaaQfpz12d9kmoM8p8oVG5wI-n_4Xghh29eJoVm2kuJ9K4seOBRNWugwqVTTSCwHuJyAM-sWD9sCOIYCrUAa0VR4Z5_XIRbCxvN4xU3aplUoRpZUssFYkxdKWgr3DXc6nD1CkrcE40wUhOk-jGVI7-QSp4_JDcceV7RYigr5q4KFKHRfI65Oznh4ucRAQw
```

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

The second section of the PASETO is the purpose (local). PASETO only defines two purposes for tokens: local or public. I’ll expand on these later. For now, just know that the local purpose is the one I’m demonstrating here.

The third section of the PASETO defines the actual token contents, also known as the payload:

## 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. By encrypting the token with a shared secret key, only the intended service can decrypt and validate the token, ensuring secure communication between services.

### 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. This method ensures that the token can be verified by anyone with the public key but only signed by the private key.

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, public PASETOs provide a secure solution. Only the server with the private key can sign the token, but any client with the public key can verify it, maintaining the token's integrity and trustworthiness.

## Difference between JWT and PASETOs:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wspoplu3h4hh7duuis1f.png)

## 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. For Maven users, search for the corresponding dependencies in the Maven repository and add them to your pom.xml.

```
    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;
    }
}
```
A PASETO token is then created using the Pasetos.V1.LOCAL.builder(). The following claims and properties are set on the token:

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

The compact() method finalizes the token creation process, returning the token as a string.

You can make get request to this API to get your PASETO.

## 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();
    }
}
```

PasetoParser is built using the Pasetos.parserBuilder() method. The parser is configured with:
1. setSharedSecret(SHARED_SECRET): The shared secret key used for symmetric encryption.
2.     setPublicKey(KEY_PAIR.getPublic()): The public key used for asymmetric encryption.
3.     requireAudience("blog"): A requirement that the token must have the audience "blog".
4.     requireIssuer("https://ezto.io/"): A requirement that the token must have the issuer "https://ezto.io/".
The token is parsed using the parser.parse(token) method, which returns a Paseto object.
If the token is valid, this method returns the issuer claim from the token.

You can make a post request with the token to this API to verify your PASETO.

## Limitations of PASETO

While PASETO (Platform-Agnostic Security Tokens) offers several improvements over JWT (JSON Web Tokens), it also has its own set of limitations. Here are some key limitations to consider:

### Ecosystem:
- The ecosystem around PASETO is not as mature as JWT’s. There are fewer implementations and integrations available for various programming languages and frameworks.
- Limited Documentation: Compared to JWT, there is less documentation, tutorials, and examples available for PASETO, which can hinder learning and adoption.

### Learning Curve:
- New Concepts: For developers familiar with JWT, PASETO introduces new concepts and a different approach to security tokens. This can require additional learning and adjustment.
- 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. This can make interoperability with existing systems challenging if they do not support PASETO.
- Tooling and Infrastructure: Existing tools, middleware, and infrastructure might not support PASETO, requiring additional effort to integrate or replace these components.

### Algorithm Support:
- Fixed Algorithms: PASETO uses fixed algorithms for each version (e.g., AES-256-GCM for local tokens in v1). While this increases security by reducing the risk of weak algorithms, it also limits flexibility for developers who might need different algorithm choices for specific use cases.
- Version Constraints: PASETO versions (v1, v2, etc.) have strict rules about which algorithms can be used, which might not suit all cryptographic needs.

### Specification Evolution:
- Standard Maturity: As PASETO is relatively new, its specification is still evolving. Future changes to the specification could introduce breaking changes or require updates to existing implementations.


In summary, exploring PASETO as an alternative to JWT reveals its potential for simplifying and enhancing token-based authentication. Understanding these differences equips developers with valuable tools to secure their applications effectively.