createJWSSession() / createJWESession()
Open a session manager from Elysia's cookie and request yourself, for a resolve, derive or guard you write by hand.
import { Elysia } from "elysia";
import { createJWSSession, generateJWK } from "unjwt/adapters/elysia";
const key = await generateJWK("HS256");
const config = { key, maxAge: "1h" };
const app = new Elysia()
.resolve(async ({ cookie, request }) => ({
session: await createJWSSession({ cookie, request }, config),
}))
.get("/login", async ({ session }) => {
await session.update({ user: "alice" });
return { id: session.id }; // { id: "fee86981-…" }
})
.get("/me", ({ session }) => ({ id: session.id ?? null, data: session.data }));
// GET /me with the cookie: { id: "fee86981-…", data: { user: "alice" } }#Signature
function createJWSSession<
T extends Record<string, any> = SessionClaims,
MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined,
>(context: SessionContext, config: SessionConfigJWS<T, MaxAge>): Promise<SessionManager<T, MaxAge>>;
function createJWESession<
T extends Record<string, any> = SessionClaims,
MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined,
>(context: SessionContext, config: SessionConfigJWE<T, MaxAge>): Promise<SessionManager<T, MaxAge>>;
interface SessionContext {
cookie: ChunkableCookieJar; // Elysia's reactive `cookie` context property
request: { headers: { get(name: string): string | null } };
}#Options
config is the same object jwsSession() and jweSession() take. contextKey has no effect here, because you choose where the manager goes. The returned manager is the one described under the session object.
#When to use it
- You want the session under a key, or with a typing, that the plugin does not give you.
- You open the session only on some routes, or only after another check, instead of on every request.
- You need the manager inside a
deriveorbeforeHandleof an app that does not mount the plugin. - You write a guard with its own rule, for example a role check, in place of
requireSession.
The plugins are thin wrappers over these functions: a scoped resolve that calls createJWSSession({ cookie, request }, config) and a macro that answers 401 while session.id is unset.
#The context argument
Destructure { cookie, request } in the hook signature, as above. Elysia decides which context fields to build by reading the hook's source, and naming cookie there keeps the jar in place.
The adapter reads cookies from the jar and writes back through each reactive cookie's set(). A cookie object that is not Elysia's jar fails on the first write:
const session = await createJWSSession({ cookie: {}, request }, { key });
await session.update({ a: 1 });
// JWTError ERR_SESSION_CONFIG_INVALID: [unjwt/elysia] Session cookie jar must expose a reactive cookie at "elysia-jws" with a set() method — pass Elysia's own `cookie` context property.#Shared state within a request
The session state lives on the request object. Every call of the same family with the same name during one request returns a manager over the same state, whether it comes from a plugin, a handler or a hook. A clear() in a hook therefore empties ctx.session too, and an update() in a hook hands the handler the new session. Calling createJWSSession() again inside the onRead hook of the same session fires onRead again and recurses, so leave sliding expiration to the handler or a beforeHandle.
Hooks receive the context you passed as context:
hooks: {
onUpdate: ({ session, context }) =>
console.log("onUpdate", session.id.slice(0, 8), "has request:", "request" in context, "has cookie:", "cookie" in context),
}
// onUpdate fee86981 has request: true has cookie: true#Errors
The same codes as the plugins. Key and config errors throw from createJWSSession() itself. A jar without reactive cookies throws ERR_SESSION_CONFIG_INVALID from the first update() or clear().