Session middleware for Hattip. It persists data between requests in a cookie or a custom session store.
This middleware uses the Web Crypto API which is supported by:
- ✅ Node.js 16 or later
- ✅ Cloudflare Workers
- ✅ Deno
- ✅ Vercel Serverless (since it uses Node.js)
- ✅ Vercel Edge (since it uses Cloudflare Workers)
- ✅ Netlify Functions (since it uses Node.js)
- ✅ Netlify Edge Functions (since it uses Deno)
- 🚧 Bun
import { cookie } from "@hattip/cookie";
import { session, SignedCookieStore } from "@hattip/session";
import { json } from "@hattip/response";
// session() requires cookie() or
// getSessionId option to be set to read
// the session ID from somewhere else
app.use(cookie());
app.use(
session({
// Session store
store: new SignedCookieStore(
await SignedCookieStore.generateKeysFromSecrets(["secret"]),
),
// Default session data when a new session is created.
// It can be a function.
// It is shallow cloned, if you need a deep clone, use a function.
defaultSessionData: {},
}),
);
app.get((ctx) => {
return json(ctx.session.data);
});
You can set the session data type using module augmentation:
declare module "@hattip/session" {
interface SessionData {
userId: string;
}
}
@hattip/session
comes bundled with a few ready-to-use session stores. Cookie stores store their data in a cookie and are appropriate for small amounts of data. External stores store a key in a cookie and use external storage to store the data. External stores are appropriate for large amounts of data.
External stores support a session.regenerate()
method, which will generate a new session ID and persist the session data to the new ID. You should call it when you want to invalidate the current session, such as when a user logs in or logs out to prevent session fixation attacks.
It stores the session data in an unsigned cookie. The client can read and modify the cookie, so it should only be used for non-sensitive data. Also, you should always validate the session data by passing a custom parse
function to the constructor:
const store = new SimpleCookieStore({
parse(data) {
const parsed = JSON.parse(data);
validate(parsed);
return parsed;
},
});
It stores the session data in a signed cookie. The client can read the cookie, but cannot modify it. It requires at least one secret key to sign the cookie. If you provide multiple keys, all of them will be used to verify the cookie but only the first one will be used to sign it. This allows you to rotate the keys without invalidating existing sessions.
You can generate keys from password-like secrets using SignedCookieStore.generateKeysFromSecrets
or you can use SignedCookieStore.generateKey
to generate a secure key. SignedCookieStore.exportKey
can be used to base64 encode a key for storing, e.g. in an environment variable. SignedCookieStore.generateKeysFromBase64
can be used to decode a series of keys from base64.
new SignedCookieStore(
await SignedCookieStore.generateKeysFromSecrets(["my new secret", "my old secret]),
);
It stores the session data in an encrypted cookie. The client cannot read or modify the cookie. It requires at least one secret key to encrypt the cookie. If you provide multiple keys, all of them will be used to decrypt the cookie but only the first one will be used to encrypt it. This allows you to rotate the keys without invalidating existing sessions.
You can use EncryptedCookieStore.generateKey
to generate a secure key. EncryptedCookieStore.exportKey
can be used to base64 encode a key for storing, e.g. in an environment variable. EncryptedCookieStore.generateKeysFromBase64
can be used to decode a series of keys from base64.
new EncryptedCookieStore(
await EncryptedCookieStore.generateKeysFromBase64([
"cnNwJHcQHlHHB7+/zRYOJP/m0JEXxQpoGskCs8Eg+XI=",
]),
);
It stores the session data in memory. On edge and serverless runtimes, this store will lose its data. On long-running servers (Node, Deno, Bun, etc.) it will leak memory and lose its data on server restart. For these reasons, it is only useful for development and should never be used in production.
It stores the session data in a Redis database. It requires a node-redis
compatible Redis client factory (a function that returns the Redis client) to be passed to the constructor.
const store = new RedisSessionStore({ getClient: () => redis.createClient() });
It stores the session data in Cloudflare Workers KV. It requires a KV namespace factory (a function that returns a KV namespace) to be passed to the constructor. For example, if your KV namespace is called SESSIONS
:
const store = new KvSessionStore({
getStore: (ctx) => ctx.platform.env.SESSIONS,
});