# JWT

The `@aziontech/jwt` package is the Azion Lib library for JSON Web Tokens (JWTs). Its three functions sign a payload into a token, verify a token and return its payload, and decode a token without checking its signature. They make no API calls and need no token. Each function returns its result directly or throws an error.

Install the package:

```bash
npm install @aziontech/jwt
```

The samples on this page are TypeScript ES modules that use top-level `await`, and they run in Node.js. They import types with `import type`, which keeps them loadable when the type annotations are stripped.

---

## Keys and algorithms

The `alg` parameter of [sign](#sign) and [verify](#verify) names the signing algorithm. Its default is `HS256`. The type declarations list 13 values: `HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`, `PS256`, `PS384`, `PS512`, `ES256`, `ES384`, `ES512`, and `EdDSA`.

A key is a [SignatureKey](#signaturekey): a `string`, a `JsonWebKey`, or a `CryptoKey`. The key you pass depends on the algorithm:

- With the default `HS256`, the key is a shared secret. Sign and verify with the same string.
- An asymmetric algorithm, such as `RS256`, takes a `CryptoKey` or a `JsonWebKey`. Sign with the private key and verify with the public key.

This sample generates an RSA key pair with `crypto.subtle`, signs a token with `RS256`, prints the token header, and verifies the token with the public key:

```typescript
import { sign, verify } from '@aziontech/jwt';

// Asymmetric algorithms take a CryptoKey (or JsonWebKey): sign with the private key, verify with the public key
const { privateKey, publicKey } = (await crypto.subtle.generateKey(
  { name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' },
  true,
  ['sign', 'verify'],
)) as CryptoKeyPair;

const token = await sign({ userId: 123 }, privateKey, 'RS256');
console.log('header:', JSON.parse(atob(token.split('.')[0])));
console.log('verified payload:', await verify(token, publicKey, 'RS256'));
```

Output:

```text
header: { alg: 'RS256', typ: 'JWT' }
verified payload: { userId: 123 }
```

---

## sign

Signs a payload and returns the token.

```typescript
function sign(payload: JWTPayload, privateKey: SignatureKey, alg?: SignatureAlgorithm): Promise<string>;
```

| Parameter    | Type                                        | Required | Description                                                                                                    |
| ------------ | ------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `payload`    | [`JWTPayload`](#jwtpayload)                 | Yes      | The claims to sign.                                                                                            |
| `privateKey` | [`SignatureKey`](#signaturekey)             | Yes      | The key that signs the token: the shared secret with `HS256`, or the private key with an asymmetric algorithm. |
| `alg`        | [`SignatureAlgorithm`](#signaturealgorithm) | No       | The signing algorithm. Default: `HS256`.                                                                       |

Returns a promise that resolves to the signed token. An `alg` value outside the list in [Keys and algorithms](#keys-and-algorithms) rejects with `JwtAlgorithmNotImplemented`.

This sample signs a payload that expires in one hour with the default `HS256`:

```typescript
import { sign } from '@aziontech/jwt';
import type { JWTPayload } from '@aziontech/jwt';

const secret: string = 'your-secret-key'; // HS256 (the default) signs with a shared secret
const payload: JWTPayload = { userId: 123, exp: Math.floor(Date.now() / 1000) + 3600 }; // 1 hour expiration
sign(payload, secret).then((token: string) => console.log(token)); // Outputs the signed JWT
```

Output:

```text
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMywiZXhwIjoxNzkxMTI1NjA0fQ.mmn-otq04QJJHbUQvm_e7b0SvNUQI98CtypKEz58kDQ
```

---

## verify

Checks the signature and the time claims of a token, and returns its payload.

```typescript
function verify(token: string, publicKey: SignatureKey, alg?: SignatureAlgorithm): Promise<JWTPayload>;
```

| Parameter   | Type                                        | Required | Description                                                                                                        |
| ----------- | ------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `token`     | `string`                                    | Yes      | The token to verify.                                                                                               |
| `publicKey` | [`SignatureKey`](#signaturekey)             | Yes      | The key that checks the signature: the shared secret with `HS256`, or the public key with an asymmetric algorithm. |
| `alg`       | [`SignatureAlgorithm`](#signaturealgorithm) | No       | The algorithm the token was signed with. Default: `HS256`.                                                         |

Returns a promise that resolves to the [JWTPayload](#jwtpayload) of a valid token. The promise rejects when the signature does not match the key, when `exp` is in the past, when `nbf` or `iat` is in the future, or when the token or its header is not valid. Each case has its own error name, listed in [Errors](#errors).

This sample signs a token, verifies it with the same secret, and then verifies it with a different secret:

```typescript
import { sign, verify } from '@aziontech/jwt';
import type { JWTPayload } from '@aziontech/jwt';

const secret: string = 'your-secret-key';
const token: string = await sign({ userId: 123, exp: Math.floor(Date.now() / 1000) + 3600 }, secret);

try {
  const payload: JWTPayload = await verify(token, secret);
  console.log(payload); // Outputs the payload if verification is successful
} catch (err) {
  console.error((err as Error).name, (err as Error).message);
}

try {
  await verify(token, 'another-secret');
} catch (err) {
  console.error((err as Error).name, (err as Error).message); // A wrong key rejects with JwtTokenSignatureMismatched
}
```

Output:

```text
{ userId: 123, exp: 1791127003 }
JwtTokenSignatureMismatched token(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMywiZXhwIjoxNzkxMTI3MDAzfQ.myf2vixa4QI6GQCxHu6h5t3UpZwMya3J1UIr2W3d8pU) signature mismatched
```

---

## decode

Reads the header and the payload of a token without verifying its signature.

```typescript
function decode(token: string): { header: TokenHeader; payload: JWTPayload };
```

| Parameter | Type     | Required | Description          |
| --------- | -------- | -------- | -------------------- |
| `token`   | `string` | Yes      | The token to decode. |

Returns an object with the [TokenHeader](#tokenheader) in `header` and the [JWTPayload](#jwtpayload) in `payload`. The function is synchronous. A string that is not a JWT throws `JwtTokenInvalid`. Because `decode` does not check the signature, use [verify](#verify) before you trust a claim.

The package does not export `TokenHeader`, so the sample lets TypeScript infer the type of the result:

```typescript
import { sign, decode } from '@aziontech/jwt';

const token: string = await sign({ userId: 123 }, 'your-secret-key');
const { header, payload } = decode(token); // decode does not verify the signature
console.log(header, payload); // Outputs the decoded header and payload
```

Output:

```text
{ alg: 'HS256', typ: 'JWT' } { userId: 123 }
```

---

## Errors

A failed call throws, or rejects with, an error whose `name` is one of the seven below. The type declarations list these error classes as exports, but the package does not export them at runtime: a file that imports one stops at load. Tell the errors apart by `err.name`, as the [verify](#verify) sample does. For the load error, refer to [Troubleshoot Azion Lib](/en/documentation/devtools/azion-lib/troubleshooting/#a-jwt-error-class-import-fails-with-does-not-provide-an-export-named).

In the messages below, `<token>` stands for the token the call received.

| Name                          | Message                                                                      | Cause                                                                | What to do                                                                             |
| ----------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `JwtAlgorithmNotImplemented`  | `none is not an implemented algorithm`                                       | The `alg` value is not one of the algorithms the package implements. | Pass a value from [Keys and algorithms](#keys-and-algorithms).                         |
| `JwtTokenInvalid`             | `invalid JWT token: abc`                                                     | The string passed to `decode` or `verify` is not a JWT.              | Pass a complete token, as `sign` returns it.                                           |
| `JwtTokenNotBefore`           | `token (<token>) is being used before it's valid`                            | The `nbf` claim is in the future.                                    | Verify the token after the time in `nbf`.                                              |
| `JwtTokenExpired`             | `token (<token>) expired`                                                    | The `exp` claim is in the past.                                      | Sign a new token with a later `exp`.                                                   |
| `JwtTokenIssuedAt`            | `Incorrect "iat" claim must be a older than "<current-time>" (iat: "<iat>")` | The `iat` claim is later than the current time.                      | Sign the token with an `iat` that is not in the future.                                |
| `JwtHeaderInvalid`            | `jwt header is invalid: {"alg":"HS256","typ":"XYZ"}`                         | The token header is not valid, such as a `typ` other than `JWT`.     | Sign the token with `sign`, which writes `typ: 'JWT'`.                                 |
| `JwtTokenSignatureMismatched` | `token(<token>) signature mismatched`                                        | The key does not match the key that signed the token.                | Verify with the same secret, or with the public key of the pair that signed the token. |

---

## Types

The package exports one type, `JWTPayload`. Import it with `import type`. The other three types below are declared by the package but not exported, so you cannot import them.

### JWTPayload

The claims of a token. Times are Unix timestamps, in seconds.

```typescript
type JWTPayload = {
  [key: string]: unknown;
  exp?: number;
  nbf?: number;
  iat?: number;
};
```

| Property        | Type      | Required | Description                                                                                         |
| --------------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `[key: string]` | `unknown` | No       | A custom claim, such as `userId`.                                                                   |
| `exp`           | `number`  | No       | The expiration time of the token. Past it, [verify](#verify) rejects with `JwtTokenExpired`.        |
| `nbf`           | `number`  | No       | The time before which the token is not valid. Before it, `verify` rejects with `JwtTokenNotBefore`. |
| `iat`           | `number`  | No       | The time the token was issued. A value in the future makes `verify` reject with `JwtTokenIssuedAt`. |

### TokenHeader

The header that [decode](#decode) returns.

```typescript
interface TokenHeader {
  alg: SignatureAlgorithm;
  typ?: 'JWT';
}
```

| Property | Type                                        | Required | Description                          |
| -------- | ------------------------------------------- | -------- | ------------------------------------ |
| `alg`    | [`SignatureAlgorithm`](#signaturealgorithm) | Yes      | The algorithm that signed the token. |
| `typ`    | `'JWT'`                                     | No       | The type of the token.               |

### SignatureAlgorithm

The algorithm names that `sign` and `verify` take in `alg`. For the 13 values, refer to [Keys and algorithms](#keys-and-algorithms).

```typescript
type SignatureAlgorithm = 'HS256' | 'HS384' | 'HS512' | 'RS256' | 'RS384' | 'RS512' | 'PS256' | 'PS384' | 'PS512' | 'ES256' | 'ES384' | 'ES512' | 'EdDSA';
```

### SignatureKey

The key that `sign` and `verify` take.

```typescript
type SignatureKey = string | JsonWebKey | CryptoKey;
```

---

## Related resources

- [Azion Lib](/en/documentation/devtools/azion-lib.md): The libraries Azion Lib ships and the package that carries each one.
- [How Azion Lib works](/en/documentation/devtools/azion-lib/how-it-works.md): Which modules call an API, which throw, and where each one runs.
- [Troubleshoot Azion Lib](/en/documentation/devtools/azion-lib/troubleshooting.md): Fixes for the load and runtime errors the Azion Lib packages return.
- [SubtleCrypto](/en/documentation/devtools/runtime/api-reference/subtle-crypto.md): The crypto.subtle methods that generate and import keys.
