koa-session
Version:
Koa cookie session middleware with external store support
284 lines • 19.2 kB
JavaScript
import assert from 'node:assert';
import { debuglog } from 'node:util';
import { randomUUID } from 'node:crypto';
import { isClass } from 'is-type-of';
import z from 'zod';
import { ContextSession } from './context.js';
import util from './util.js';
const debug = debuglog('koa-session');
const GET_CONTEXT_SESSION = Symbol('get contextSession');
const CONTEXT_SESSION_INSTANCE = Symbol('contextSession instance');
export const SessionOptions = z.object({
/**
* cookie key
* Default is `koa.sess`
*/
key: z.string().default('koa.sess'),
/**
* maxAge in ms
* Default is `86400000`, one day
* If set to 'session' will result in a cookie that expires when session/browser is closed
*
* Warning: If a session cookie is stolen, this cookie will never expire
*/
maxAge: z.union([z.number(), z.literal('session')]).optional(),
/**
* automatically commit headers
* Default is `true`
*/
autoCommit: z.boolean().default(true),
/**
* cookie value can overwrite or not
* Default is `true`
*/
overwrite: z.boolean().default(true),
/**
* httpOnly or not
* Default is `true`
*/
httpOnly: z.boolean().default(true),
/**
* signed or not
* Default is `true`
*/
signed: z.boolean().default(true),
/**
* Force a session identifier cookie to be set on every response.
* The expiration is reset to the original `maxAge`, resetting the expiration countdown.
* Default is `false`
*/
rolling: z.boolean().default(false),
/**
* renew session when session is nearly expired, so we can always keep user logged in.
* Default is `false`
*/
renew: z.boolean().default(false),
/**
* secure cookie
* Default is `undefined`, will be set to `true` if the connection is over HTTPS, otherwise `false`.
*/
secure: z.boolean().optional(),
/**
* session cookie sameSite options
* Default is `undefined`, meaning don't set it
*/
sameSite: z.string().optional(),
/**
* External key is used the cookie by default,
* but you can use `options.externalKey` to customize your own external key methods.
*/
externalKey: z.object({
/**
* get the external key
* `(ctx) => string`
*/
get: z.function()
.args(z.any())
.returns(z.string()),
/**
* set the external key
* `(ctx, key) => void`
*/
set: z.function()
.args(z.any(), z.string())
.returns(z.void()),
}).optional(),
/**
* session storage is dependent on your external store
*/
store: z.object({
/**
* get session data by key
* `(key, maxAge, { rolling, ctx }) => sessionData | Promise<sessionData>`
*/
get: z.function()
.args(z.string(), z.number(), z.object({ rolling: z.boolean(), ctx: z.any() }))
.returns(z.promise(z.any())),
/**
* set session data for key, with a `maxAge` (in ms)
* `(key, sess, maxAge, { rolling, changed, ctx }) => void | Promise<void>`
*/
set: z.function()
.args(z.string(), z.any(), z.number(), z.object({ rolling: z.boolean(), changed: z.boolean(), ctx: z.any() }))
.returns(z.promise(z.void())),
/**
* destroy session data for key
* `(key, { ctx })=> void | Promise<void>`
*/
destroy: z.function()
.args(z.string(), z.object({ ctx: z.any() }))
.returns(z.promise(z.void())),
}).optional(),
/**
* If your session store requires data or utilities from context, `opts.ContextStore` is also supported.
* `ContextStore` must be a class which claims three instance methods demonstrated above.
* `new ContextStore(ctx)` will be executed on every request.
*/
ContextStore: z.any().optional(),
encode: z.function()
.args(z.any())
.returns(z.string())
.optional()
.default(() => util.encode),
decode: z.function()
.args(z.string())
.returns(z.any())
.default(() => util.decode),
/**
* If you want to generate a new session id, you can use `genid` option to customize it.
* Default is a function that uses `randomUUID()`.
* `(ctx) => string`
*/
genid: z.function()
.args(z.any())
.returns(z.string())
.optional(),
/**
* If you want to prefix the session id, you can use `prefix` option to customize it.
* It will not work if `options.genid(ctx)` present.
*/
prefix: z.string().optional(),
/**
* valid session value before use it
* `(ctx, sessionData) => boolean`
*/
valid: z.function()
.args(z.any(), z.any())
.returns(z.any())
.optional(),
/**
* hook before save session
* `(ctx, sessionModel) => void`
*/
beforeSave: z.function()
.args(z.any(), z.any())
.returns(z.void())
.optional(),
});
const DEFAULT_SESSION_OPTIONS = SessionOptions.parse({});
export function createSession(opts, app) {
// session(app[, opts])
if (opts && 'use' in opts && typeof opts.use === 'function') {
[] = [opts, app];
}
// app required
if (typeof app?.use !== 'function') {
throw new TypeError('app instance required: `session(opts, app)`');
}
const options = opts ?? {};
// back-compat maxage
if (!('maxAge' in options) && 'maxage' in options) {
Reflect.set(options, 'maxAge', Reflect.get(options, 'maxage'));
if (process.env.NODE_ENV !== 'production') {
console.warn('[koa-session] DeprecationWarning: `maxage` option has been renamed to `maxAge`');
}
}
// keep backwards compatibility: make sure options instance is not mutated
Object.assign(options, {
...DEFAULT_SESSION_OPTIONS,
...options,
});
SessionOptions.parse(options);
formatOptions(options);
extendContext(app.context, options);
return async function session(ctx, next) {
const sess = ctx[GET_CONTEXT_SESSION];
if (sess.store) {
await sess.initFromExternal();
}
try {
await next();
}
catch (err) {
throw err;
}
finally {
if (options.autoCommit) {
await sess.commit();
}
}
};
}
// Usage: `import session from 'koa-session'`
export default createSession;
/**
* format and check session options
*/
function formatOptions(opts) {
// defaults
if (opts.overwrite == null)
opts.overwrite = true;
if (opts.httpOnly == null)
opts.httpOnly = true;
// delete null sameSite config
if (opts.sameSite == null)
delete opts.sameSite;
if (opts.signed == null)
opts.signed = true;
if (opts.autoCommit == null)
opts.autoCommit = true;
debug('session options %j', opts);
const store = opts.store;
if (store) {
assert(typeof store.get === 'function', 'store.get must be function');
assert(typeof store.set === 'function', 'store.set must be function');
assert(typeof store.destroy === 'function', 'store.destroy must be function');
}
const externalKey = opts.externalKey;
if (externalKey) {
assert(typeof externalKey.get === 'function', 'externalKey.get must be function');
assert(typeof externalKey.set === 'function', 'externalKey.set must be function');
}
const ContextStore = opts.ContextStore;
if (ContextStore) {
assert(isClass(ContextStore), 'ContextStore must be a class');
assert(typeof ContextStore.prototype.get === 'function', 'ContextStore.prototype.get must be function');
assert(typeof ContextStore.prototype.set === 'function', 'ContextStore.prototype.set must be function');
assert(typeof ContextStore.prototype.destroy === 'function', 'ContextStore.prototype.destroy must be function');
}
if (!opts.genid) {
if (opts.prefix) {
opts.genid = () => `${opts.prefix}${randomUUID()}`;
}
else {
opts.genid = () => randomUUID();
}
}
}
/**
* extend context prototype, add session properties
*
* @param {Object} context koa's context prototype
* @param {Object} opts session options
*/
function extendContext(context, opts) {
if (context.hasOwnProperty(GET_CONTEXT_SESSION)) {
return;
}
Object.defineProperties(context, {
[]: {
get() {
if (this[CONTEXT_SESSION_INSTANCE]) {
return this[CONTEXT_SESSION_INSTANCE];
}
this[CONTEXT_SESSION_INSTANCE] = new ContextSession(this, opts);
return this[CONTEXT_SESSION_INSTANCE];
},
},
session: {
get() {
return this[GET_CONTEXT_SESSION].get();
},
set(val) {
this[GET_CONTEXT_SESSION].set(val);
},
configurable: true,
},
sessionOptions: {
get() {
return this[GET_CONTEXT_SESSION].opts;
},
},
});
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxNQUFNLE1BQU0sYUFBYSxDQUFDO0FBQ2pDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDckMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUN6QyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQ3JDLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQztBQUNwQixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQzlDLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUU3QixNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQUM7QUFFdEMsTUFBTSxtQkFBbUIsR0FBRyxNQUFNLENBQUMsb0JBQW9CLENBQUMsQ0FBQztBQUN6RCxNQUFNLHdCQUF3QixHQUFHLE1BQU0sQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO0FBRW5FLE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQ3JDOzs7T0FHRztJQUNILEdBQUcsRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQztJQUNuQzs7Ozs7O09BTUc7SUFDSCxNQUFNLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFFLENBQUMsQ0FBQyxRQUFRLEVBQUU7SUFDaEU7OztPQUdHO0lBQ0gsVUFBVSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDO0lBQ3JDOzs7T0FHRztJQUNILFNBQVMsRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUMsT0FBTyxDQUFD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