# Utilities

> 

Small but pervasive helpers — base64url encoding, cryptographic randomness, type guards, JWT claim validation, and the `Duration` format that powers `expiresIn` / `maxTokenAge` / `notBeforeIn` everywhere in unjwt.

```ts
import {
  base64UrlEncode,
  base64UrlDecode,
  base64Encode,
  base64Decode,
  secureRandomBytes,
  concatUint8Arrays,
  textEncoder,
  textDecoder,
  isJWK,
  isJWKSet,
  isSymmetricJWK,
  isAsymmetricJWK,
  isPrivateJWK,
  isPublicJWK,
  isCryptoKey,
  isCryptoKeyPair,
  validateJwtClaims,
  computeDurationInSeconds,
  inferJWSAllowedAlgorithms,
  inferJWEAllowedAlgorithms,
  sanitizeObject,
} from "unjwt/utils";
```

All of these are also available from the flat barrel `unjwt`.

## Encoding

### `base64UrlEncode(data)`

Encodes `Uint8Array | string` to **Base64URL** (no padding, URL-safe alphabet). Returns a `string`.

```ts
base64UrlEncode(new Uint8Array([255, 255])); // "__8"
base64UrlEncode("hello"); // "aGVsbG8"
```

### `base64UrlDecode(data, options?)`

Decodes a Base64URL string or `Uint8Array`. Without an options object the return type follows the input: a `string` input returns a decoded `string`, a `Uint8Array` input returns a `Uint8Array`. Pass `{ returnAs: "string" }` or `{ returnAs: "uint8array" }` (alias `"bytes"`) to force the return type.

```ts
base64UrlDecode("aGVsbG8"); // "hello"
base64UrlDecode("aGVsbG8", { returnAs: "uint8array" });
// Uint8Array([104, 101, 108, 108, 111])
base64UrlDecode(new Uint8Array([97, 71, 86, 115, 98, 71, 56]), { returnAs: "string" });
// "hello"
```

### `base64Encode(data)` / `base64Decode(data, options?)`

Standard Base64 (with `=` padding). Same signatures as the URL-safe variants — `base64Decode` accepts the same `{ returnAs }` options object as `base64UrlDecode`. Use when interoperating with systems that expect classical Base64.

## Randomness

### `secureRandomBytes`

```ts
secureRandomBytes(length);
```

Returns a cryptographically secure `Uint8Array` of the specified length, via [`crypto.getRandomValues`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues):

```ts
const salt = secureRandomBytes(16); // 128-bit random salt
const cek = secureRandomBytes(32); // 256-bit CEK
```

Use this — not `Math.random()`, not a Node-specific API — for any security-sensitive randomness. It works everywhere unjwt runs.

<note>

There is no `randomBytes` alias — use `secureRandomBytes`.

</note>

## Concatenation

### `concatUint8Arrays(...arrays)`

Concatenates multiple `Uint8Array` instances into one. Useful when building custom envelopes or AAD strings without intermediate `Buffer` allocations:

```ts
const aad = concatUint8Arrays(
  textEncoder.encode(base64UrlEncode(protectedHeader)),
  new Uint8Array([0x2e]), // "."
  textEncoder.encode(base64UrlEncode(externalAad)),
);
```

## Singletons

### `textEncoder` / `textDecoder`

Singleton `TextEncoder` and `TextDecoder` instances — reuse them instead of allocating new ones per call:

```ts
textEncoder.encode("hello"); // Uint8Array
textDecoder.decode(uint8ArrayBytes); // string
```

## Type guards

<table>
<thead>
  <tr>
    <th>
      Function
    </th>
    
    <th>
      Returns
    </th>
    
    <th>
      Checks
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        isJWK(key)
      </code>
    </td>
    
    <td>
      <code>
        key is JWK
      </code>
    </td>
    
    <td>
      Object with <code>
        kty
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isJWKSet(key)
      </code>
    </td>
    
    <td>
      <code>
        key is JWKSet
      </code>
    </td>
    
    <td>
      Object with <code>
        keys: JWK[]
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isCryptoKey(key)
      </code>
    </td>
    
    <td>
      <code>
        key is CryptoKey
      </code>
    </td>
    
    <td>
      <code>
        CryptoKey
      </code>
      
       instance
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isCryptoKeyPair(key)
      </code>
    </td>
    
    <td>
      <code>
        key is CryptoKeyPair
      </code>
    </td>
    
    <td>
      Object with <code>
        publicKey
      </code>
      
       + <code>
        privateKey
      </code>
      
       as <code>
        CryptoKey
      </code>
      
      s
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isSymmetricJWK(key)
      </code>
    </td>
    
    <td>
      <code>
        key is JWK_oct
      </code>
    </td>
    
    <td>
      JWK with <code>
        kty: "oct"
      </code>
      
       and <code>
        k
      </code>
      
       property
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isAsymmetricJWK(key)
      </code>
    </td>
    
    <td>
      <code>
        key is JWK_Asymmetric
      </code>
    </td>
    
    <td>
      JWK that isn't <code>
        oct
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isPrivateJWK(key)
      </code>
    </td>
    
    <td>
      <code>
        key is JWK_Private
      </code>
    </td>
    
    <td>
      Asymmetric JWK with a <code>
        d
      </code>
      
       (private component)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isPublicJWK(key)
      </code>
    </td>
    
    <td>
      <code>
        key is JWK_Public
      </code>
    </td>
    
    <td>
      Asymmetric JWK without <code>
        d
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        assertCryptoKey(key)
      </code>
    </td>
    
    <td>
      assertion
    </td>
    
    <td>
      Throws if not a <code>
        CryptoKey
      </code>
    </td>
  </tr>
</tbody>
</table>

```ts [guards.ts]
import { isPrivateJWK, isSymmetricJWK } from "unjwt/utils";

function canSign(k: unknown) {
  return isSymmetricJWK(k) || isPrivateJWK(k);
}
```

## JWT claim validation

### `validateJwtClaims`

```ts
validateJwtClaims(claims, options?)
```

The low-level claim validator used inside `verify()` and `decrypt()`. Throws `JWTError` on failure. Checks:

- `exp` — expiration
- `nbf` — not-before
- `iat` — issued-at type check and (with `maxTokenAge`) age bound
- `iss` — issuer
- `sub` — subject
- `aud` — audience
- `requiredClaims` — presence of a named set

Timestamps (`exp`, `nbf`, `iat`) are validated **strictly**: if present but not a finite number (string, `NaN`, `null`), the check throws rather than silently skipping.

<note>

`typ` is part of {@link JWTClaimValidationOptions} but is validated at the `verify()` / `decrypt()` layer (against the protected header), not inside `validateJwtClaims` itself.

</note>

### `JWTClaimValidationOptions`

```ts
interface JWTClaimValidationOptions {
  audience?: string | string[];
  issuer?: string | string[];
  subject?: string;
  maxTokenAge?: MaxTokenAge; // alias of Duration
  clockTolerance?: number; // seconds; defaults to 0
  typ?: string;
  currentDate?: Date;
  requiredClaims?: string[];
  /**
   * Critical header parameters this caller understands.
   * Verification fails if the token's `crit` header lists a parameter
   * not in this list (RFC 7515 §4.1.11 / RFC 7516 §4.1.13).
   */
  recognizedHeaders?: string[];
}
```

This same interface is extended by [`JWSVerifyOptions`](/jwt/jws/verifying) and [`JWEDecryptOptions`](/jwt/jwe/decrypting) — so the options below work identically everywhere.

## The `Duration` format

`expiresIn`, `notBeforeIn`, `maxTokenAge`, and friends all accept a single type:

```ts
type Duration = number | `${number}` | `${number}${Unit}`;

// Unit short forms: s m h D W M Y
// Unit long forms: seconds minutes hours days weeks months years
// (both singular and plural accepted for long forms)
```

Number and unit must be adjacent — no space. Case matters for the single-letter day/week/month/year units: `D`, `W`, `M`, `Y`.

Examples that all resolve to 3600 seconds:

```ts
3600
"3600"    "3600s"    "3600seconds"
"60m"     "60minutes"
"1h"      "1hour"
```

And others:

```ts
"7D"; // 7 days
"1W"; // 1 week
"3M"; // 3 months (30-day months)
"1Y"; // 1 year (365-day years)
"30s"; // 30 seconds
```

### `computeDurationInSeconds(duration)`

Converts a `Duration` to a positive integer number of seconds. Throws if the result is `<= 0`:

```ts
computeDurationInSeconds("1h"); // 3600
computeDurationInSeconds("7D"); // 604800
computeDurationInSeconds(30); // 30
```

<note>

`computeMaxTokenAgeSeconds` and `computeExpiresInSeconds` are `@deprecated` aliases retained for backward compatibility. Prefer `computeDurationInSeconds` in new code.

</note>

## Algorithm inference

Used internally to derive allowlists from key shapes when `options.algorithms` is omitted. Exposed for advanced use — e.g. building a logging layer that reports which algorithms your infrastructure actually exercises.

### `inferJWSAllowedAlgorithms`

```ts
inferJWSAllowedAlgorithms(key);
```

Returns the set of signing algorithms a key can unambiguously produce, or `undefined` if inference isn't possible (raw bytes, alg-less JWK, lookup function).

### `inferJWEAllowedAlgorithms`

```ts
inferJWEAllowedAlgorithms(key);
```

Returns the set of key-management algorithms a key can unambiguously handle. Passwords infer to the three `PBES2-*` variants plus `"dir"`; symmetric keys infer to their specific wrap alg plus `"dir"`.

When these return `undefined`, callers **must** pass `options.algorithms` explicitly — otherwise verification / decryption has no effective algorithm guard.

## `sanitizeObject`

```ts
sanitizeObject(obj);
```

Returns a deep structural copy of `obj` with the prototype-pollution vectors `__proto__`, `prototype`, and `constructor` stripped at every level. Input is never modified.

unjwt applies this internally to all parsed JWT headers and user-supplied options — you rarely need to call it yourself. Exposed for userland code that handles untrusted JSON.

## See also

- [Getting started: core concepts →](/getting-started/core-concepts) — a plain-language walkthrough of the claims this module validates.
- [JWS verifying →](/jwt/jws/verifying) — the claim validation options in context.
- [JWE decrypting →](/jwt/jwe/decrypting) — same options on the encrypted side.
