# Adapters

> 

unjwt ships cookie-based **session adapters** for [H3](https://h3.dev) — the tiny universal HTTP framework behind [Nuxt](https://nuxt.com) and [Nitro](https://nitro.build). Both H3 v1 and v2 are supported (used by different Nuxt / Nitro major versions).

Import paths:

<table>
<thead>
  <tr>
    <th>
      Path
    </th>
    
    <th>
      Framework
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        unjwt/adapters/h3v1
      </code>
    </td>
    
    <td>
      <strong>
        H3 v1
      </strong>
      
       — Nuxt v4, Nitro v2
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unjwt/adapters/h3v2
      </code>
    </td>
    
    <td>
      <strong>
        H3 v2
      </strong>
      
       — Nuxt v5, Nitro v3
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unjwt/adapters/h3
      </code>
    </td>
    
    <td>
      Alias for <code>
        h3v1
      </code>
      
       (will flip to v2 in a future major release)
    </td>
  </tr>
</tbody>
</table>

All three export the same API surface. The internal difference is which `h3` peer dependency they're compiled against.

<note>

Peer dep: `h3` — `^1.15.4` for v1, `>=2.0.1-rc.20 || ^2.0.1` for v2.

</note>

## JWE vs JWS sessions

Each adapter provides two entry points:

<table>
<thead>
  <tr>
    <th>
      Function
    </th>
    
    <th>
      Session storage
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <a href="/adapters/h3-sessions">
        <code>
          useJWESession
        </code>
      </a>
    </td>
    
    <td>
      JWE — encrypted, opaque to the client.
    </td>
  </tr>
  
  <tr>
    <td>
      <a href="/adapters/h3-sessions">
        <code>
          useJWSSession
        </code>
      </a>
    </td>
    
    <td>
      JWS — signed, readable by the client.
    </td>
  </tr>
</tbody>
</table>

<table>
<thead>
  <tr>
    <th>
      
    </th>
    
    <th>
      JWE (encrypted)
    </th>
    
    <th>
      JWS (signed)
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Data visibility
    </td>
    
    <td>
      Encrypted, not readable by client
    </td>
    
    <td>
      Base64URL-encoded, readable by anyone
    </td>
  </tr>
  
  <tr>
    <td>
      Default cookie
    </td>
    
    <td>
      <code>
        httpOnly: true, secure: true
      </code>
    </td>
    
    <td>
      <code>
        httpOnly: false, secure: true
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Key types
    </td>
    
    <td>
      Password string, symmetric JWK, asymmetric keypair
    </td>
    
    <td>
      Symmetric JWK, asymmetric keypair
    </td>
  </tr>
  
  <tr>
    <td>
      Use when
    </td>
    
    <td>
      Session data is sensitive
    </td>
    
    <td>
      Data is non-sensitive; clients need to read it
    </td>
  </tr>
</tbody>
</table>

Rule of thumb:

- **Login / refresh tokens** — JWE. The client shouldn't inspect them, and they often contain user-identifiable data.
- **Session data the UI should render from** — JWS. Things like role, display name, theme — readable, tamper-evident.
- **Mixing both** — common in practice. See the [refresh-token pattern](/examples/refresh-token-pattern).

## The session manager

Both `useJWESession` and `useJWSSession` return a `SessionManager`:

```ts
interface SessionManager<T, ConfigMaxAge> {
  readonly id: string | undefined; // from jti — undefined until update() is called
  readonly createdAt: number; // from iat, in ms
  readonly expiresAt: ConfigMaxAge extends ExpiresIn ? number : number | undefined;
  readonly data: SessionData<T>; // session payload (excludes jti/iat/exp)
  readonly token: string | undefined; // current raw JWT token
  update: (update?: SessionUpdate<T>) => Promise<SessionManager<T, ConfigMaxAge>>;
  clear: () => Promise<SessionManager<T, ConfigMaxAge>>;
}
```

Sessions are **lazy** — `session.id` is `undefined` until you call `session.update()`. This is intentional: it matches OAuth and spec-compliant flows where a session only exists once a valid operation has created it.

## What else the adapters do

- **Automatic cookie chunking** for large tokens (essential for JWE with claims that exceed 4KB).
- **Header-based token extraction** as an alternative to cookies (`Authorization: Bearer ...`).
- **Lifecycle hooks** — `onRead`, `onUpdate`, `onClear`, `onExpire`, `onError`.
- **Key-lookup hooks** — `onVerifyKeyLookup` (JWS) / `onUnsealKeyLookup` (JWE) for key rotation.
- **Lower-level helpers** — `getJWESession`, `sealJWESession`, `unsealJWESession`, etc., when you need manual control.
- **Re-exported helpers** — `generateJWK`, `importPEM`, `exportPEM`, `deriveJWKFromPassword` for convenience.

## Going further

- [H3 sessions →](/adapters/h3-sessions) — `useJWESession` / `useJWSSession` configuration in detail.
- [Lifecycle hooks →](/adapters/hooks) — logging, revocation, key rotation.
- [Lower-level functions →](/adapters/lower-level) — manual session control.
- [Example: refresh token pattern →](/examples/refresh-token-pattern).
