# Lower-level functions

> 

`useJWESession` and `useJWSSession` are the high-level path — they wire up reading, hooks, cookies, and the `SessionManager` in one call. For the cases where you need **explicit control** over those steps (custom flows, testing, one-off token minting, external session stores), unjwt exposes the underlying building blocks.

Import:

```ts
import {
  getJWESession,
  getJWESessionToken,
  updateJWESession,
  sealJWESession,
  unsealJWESession,
  clearJWESession,
  getJWSSession,
  updateJWSSession,
  signJWSSession,
  verifyJWSSession,
  clearJWSSession,
} from "unjwt/adapters/h3v2";
```

Every function takes the H3 event and the matching `SessionConfigJWE` / `SessionConfigJWS` (the same one you'd pass to the high-level `useX` helper).

## JWE functions

<table>
<thead>
  <tr>
    <th>
      Function
    </th>
    
    <th>
      Purpose
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        getJWESession(event, config)
      </code>
    </td>
    
    <td>
      Read/initialize session from cookie or header
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        getJWESessionToken(event, config)
      </code>
    </td>
    
    <td>
      Get raw token string (no decryption)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        updateJWESession(event, config, update?)
      </code>
    </td>
    
    <td>
      Update data, re-encrypt, set cookie
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sealJWESession(event, config)
      </code>
    </td>
    
    <td>
      Encrypt current session to JWE token string
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unsealJWESession(event, config, token)
      </code>
    </td>
    
    <td>
      Decrypt a JWE token to session data
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        clearJWESession(event, config)
      </code>
    </td>
    
    <td>
      Delete session from context and expire cookie
    </td>
  </tr>
</tbody>
</table>

## JWS functions

<table>
<thead>
  <tr>
    <th>
      Function
    </th>
    
    <th>
      Purpose
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        getJWSSession(event, config)
      </code>
    </td>
    
    <td>
      Read/initialize session from cookie or header
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        updateJWSSession(event, config, update?)
      </code>
    </td>
    
    <td>
      Update data, re-sign, set cookie
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        signJWSSession(event, config)
      </code>
    </td>
    
    <td>
      Sign current session to JWS token string
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        verifyJWSSession(event, config, token)
      </code>
    </td>
    
    <td>
      Verify a JWS token to session data
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        clearJWSSession(event, config)
      </code>
    </td>
    
    <td>
      Delete session from context and expire cookie
    </td>
  </tr>
</tbody>
</table>

## Typical uses

### Mint a one-off token without cookies

For generating a token to send via email (magic link, password reset), you don't want the cookie side-effects of `useJWSSession`:

```ts [magic-link.ts]
import { signJWSSession } from "unjwt/adapters/h3v2";

app.post("/request-magic-link", async (event) => {
  const email = (await event.req.json()).email;

  const token = await signJWSSession(event, {
    key: magicLinkKey,
    maxAge: "15m",
    cookie: false, // don't set a cookie
    sessionHeader: false, // don't read one either
  });

  await sendEmail(email, `https://app.example.com/login?token=${token}`);
  return { ok: true };
});
```

### Access the refresh token from an access-token hook

The [refresh-token example](/examples/refresh-token-pattern) uses this pattern — from inside the access token's `onExpire`, peek at the refresh session without creating a new `SessionManager`:

```ts [refresh-hook.ts]
hooks: {
  async onExpire({ event, config }) {
    const refresh = await getJWESession(event, refreshConfig);
    if (!refresh.data.sub) return;

    await updateJWSSession(event, config, {
      sub: refresh.data.sub,
      scope: refresh.data.scope,
    });
  },
},
```

`getJWESession` returns the same shape as the high-level `useJWESession`, but you're in control of when it runs.

### Verify an externally-sourced token

You received a token through some channel other than the default cookie/header — a webhook body, a query parameter, a separate header — and want to verify it using your session config:

```ts [external.ts]
import { verifyJWSSession } from "unjwt/adapters/h3v2";

app.post("/webhook", async (event) => {
  const payload = await event.req.json();
  const token = payload.signedEvent;

  const session = await verifyJWSSession(event, config, token);
  if (!session.data.userId) {
    throw new HTTPError("Invalid signed event", { status: 403 });
  }

  // process payload...
});
```

### Roll a session over to a different key

For scheduled key rotation, you can clear the session with the old key and re-seal with a new config:

```ts [key-roll.ts]
// old config (before rotation)
const old = { key: oldKey, maxAge: "7D" } satisfies SessionConfigJWE;

// new config (after rotation)
const nu = { key: newKey, maxAge: "7D" } satisfies SessionConfigJWE;

app.post("/rotate-keys", async (event) => {
  const session = await getJWESession(event, old);
  if (!session.data.userId) throw new HTTPError("Not authenticated", { status: 401 });

  const data = session.data;
  await clearJWESession(event, old);
  await updateJWESession(event, nu, data);

  return { ok: true };
});
```

## When to stay high-level

Reach for the lower-level functions only when you need the specific behavior they expose. For anything that fits the "one session per request" shape, `useJWESession` / `useJWSSession` — plus hooks — will be shorter and less error-prone.

## See also

- [H3 sessions →](/adapters/h3-sessions)
- [Lifecycle hooks →](/adapters/hooks)
- [Example: refresh-token pattern →](/examples/refresh-token-pattern)
