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:

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

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

JWS functions

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

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:

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 uses this pattern — from inside the access token's onExpire, peek at the refresh session without creating a new SessionManager:

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:

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:

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