Lower-level functions
The single steps useJWSSession() and useJWESession() are made of, for reading, issuing, minting and verifying session tokens by hand.
import { generateJWK, getJWSSession, updateJWSSession, clearJWSSession } from "unjwt/adapters/h3v2";
const key = await generateJWK("HS256");
const config = { key, maxAge: "15m" };
// in a handler
const issued = await updateJWSSession(event, config, { user: "alice" });
// { id: "a1d9390e-…", token: "eyJraWQiOiJ1UklV…", expiresAt: 1792069421000, … }
// in the next request
const session = await getJWSSession(event, config);
// { id: "a1d9390e-…", data: { user: "alice" }, … }
await clearJWSSession(event, config);
// Set-Cookie: h3-jws=; Max-Age=0; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; SameSite=LaxBoth unjwt/adapters/h3v1 and unjwt/adapters/h3v2 export these functions. They take the same config as useJWSSession() and share its per-request state, so a manager and a lower-level call on the same event see the same session.
#Signatures
// JWS sessions
function getJWSSession<T, MaxAge>(
event,
config: SessionConfigJWS<T, MaxAge>,
): Promise<SessionJWS<T, MaxAge>>;
function getJWSSessionToken<T, MaxAge>(
event,
config: SessionConfigJWS<T, MaxAge>,
): string | undefined;
function updateJWSSession<T, MaxAge>(
event,
config: SessionConfigJWS<T, MaxAge>,
update?: SessionUpdate<T>,
options?: { maxAge?: ExpiresIn },
): Promise<SessionJWS<T, MaxAge> & { id: string; token: string }>;
function signJWSSession<T, MaxAge>(event, config: SessionConfigJWS<T, MaxAge>): Promise<string>;
function verifyJWSSession<T, MaxAge>(
event,
config: SessionConfigJWS<T, MaxAge>,
token: string,
): Promise<Partial<SessionJWS<T, MaxAge>>>;
function clearJWSSession<T, MaxAge>(event, config: SessionConfigJWS<T, MaxAge>): Promise<void>;
// JWE sessions: the same shapes
(getJWESession,
getJWESessionToken,
updateJWESession,
sealJWESession,
unsealJWESession,
clearJWESession);SessionJWS and SessionJWE are the plain session state (id, createdAt, expiresAt, data, token), without the update() and clear() methods of the manager.
#What each one does
| Function | Reads request | Writes cookie | Fires hooks | Returns |
|---|---|---|---|---|
getJWSSession / getJWESession | yes | only to expire an expired token | onRead, onExpire, onError | The session state. |
getJWSSessionToken / getJWESessionToken | yes | no | none | The raw token, or undefined. |
updateJWSSession / updateJWESession | if not yet read | yes | read hooks on a first read, then onUpdate or onError | The new state with id and token. |
signJWSSession / sealJWESession | if not yet read | no | read hooks on a first read | A compact token for the current session. |
verifyJWSSession / unsealJWESession | no | no | key lookup only | { id, createdAt, expiresAt, data }. |
clearJWSSession / clearJWESession | no | yes, Max-Age=0 | onClear | Nothing. |
useJWSSession() is getJWSSession() plus a manager whose update() and clear() call updateJWSSession() and clearJWSSession().
#getJWSSessionToken()
Returns the token the session would read: a cookie this response already set, then the request cookie (chunks joined), then the session header. After a clear() in the same request it returns undefined, header included. It checks nothing. Use it to forward the token to another service that verifies it on its own.
#clearJWSSession()
Expires the cookie whether or not the session was read. onClear receives the session as it stood in this request, so a clear with no earlier read passes oldSession: undefined. Open the session first when the hook needs the old id.
#updateJWSSession()
The same operation as session.update(), for code that has the event and the config but no manager. Inside a hook, session.update() on the manager the hook receives does the same thing.
#signJWSSession() and sealJWESession()
Encode the current session as a compact token without writing a cookie and without firing onUpdate. After an update in the same request the result carries the same jti, iat and exp as the cookie. Use it to hand the token to a client in a response body:
const issued = await updateJWSSession(event, config, { user: "alice" });
const token = await signJWSSession(event, config);
// the same jti as issued.id; for an HMAC key, the same token as issued.tokenA session that has no id yet gets a fresh jti on every call, and that jti is not stored. A config with cookie: false and sessionHeader: false has nothing to read, so these functions then encode an empty session: a one-off token with only jti, iat and exp.
#verifyJWSSession() and unsealJWESession()
Open a token that did not come from this request's cookie or header, such as one posted in a body or received over a WebSocket. The checks are those of a session read: the session typ, the pinned alg (and enc), jti and iat, and exp when maxAge is set. The request's session is left alone, and only the key lookup hook runs.
const opened = await verifyJWSSession(event, config, token);
// { id: "7246385f-…", createdAt: 1792068521000, expiresAt: 1792068821000, data: {} }Failures throw, since there is no onError to receive them:
| Input | Throws |
|---|---|
| An expired session token | ERR_JWT_EXPIRED |
| A malformed token | ERR_SESSION_TOKEN_INVALID, cause ERR_JWS_INVALID |
| A tampered JWE | ERR_SESSION_TOKEN_INVALID, cause ERR_JWE_DECRYPTION_FAILED |
#Request-like events on h3 v1
The h3 v1 entry also accepts an object that is not an h3 event, as long as it carries context and either headers or request.headers. Reading works. Writing does not, because such an object has no response:
import { getJWSSession, useJWSSession } from "unjwt/adapters/h3v1";
const event = { headers: new Headers({ cookie: `h3-jws=${token}` }), context: {} };
(await getJWSSession(event, config)).id; // "76755fff-…"
const session = await useJWSSession(event, config);
await session.update({ a: 1 });
// JWTError ERR_SESSION_READONLY_EVENT: [unjwt/h3] Cannot update session on read-only event.#Errors
These functions throw the same codes as useJWSSession(). getJWSSessionToken() and clearJWSSession() also throw ERR_SESSION_CONFIG_INVALID when cookie and sessionHeader are both false. verifyJWSSession() and unsealJWESession() throw where the other functions report to hooks, as listed above.