UNPKG

next-iron-session

Version:

Next.js stateless session utility using signed and encrypted cookies to store data

169 lines (143 loc) 4.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.applySession = applySession; exports.withIronSession = withIronSession; exports.ironSession = ironSession; var _ironStore = _interopRequireDefault(require("iron-store")); var _cookie = _interopRequireDefault(require("cookie")); var _debug = _interopRequireDefault(require("debug")); var _arrayPrototype = _interopRequireDefault(require("array.prototype.flat")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const debug = (0, _debug.default)("next-iron-session"); // default time allowed to check for iron seal validity when ttl passed // see https://hapi.dev/family/iron/api/?v=6.0.0#options const timestampSkewSec = 60; function throwOnNoPassword() { throw new Error("next-iron-session: Missing parameter `password`"); } function throwOnNoCookieName() { throw new Error("next-iron-session: Missing parameter `cookieName`"); } function computeCookieMaxAge(ttl) { // The next line makes sure browser will expire cookies before seals are considered expired by the server. // It also allows for clock difference of 60 seconds maximum between server and clients. return (ttl === 0 ? 2147483647 : ttl) - timestampSkewSec; } function getCookieOptions({ userCookieOptions, ttl }) { const defaultCookieOptions = { httpOnly: true, secure: true, sameSite: "lax", path: "/" }; return { ...defaultCookieOptions, ...userCookieOptions, maxAge: userCookieOptions.maxAge || computeCookieMaxAge(ttl) }; } async function applySession(req, res, { ttl = 15 * 24 * 3600, cookieName = throwOnNoCookieName(), password = throwOnNoPassword(), cookieOptions: userCookieOptions = {} }) { const cookieOptions = getCookieOptions({ userCookieOptions, ttl }); const store = await getOrCreateStore({ sealed: _cookie.default.parse(req.headers.cookie || "")[cookieName], password, ttl: ttl * 1000 }); const session = { set: store.set, get: store.get, unset: store.unset, async save() { const seal = await store.seal(); const cookieValue = _cookie.default.serialize(cookieName, seal, cookieOptions); if (cookieValue.length > 4096) { throw new Error(`next-iron-session: Cookie length is too big ${cookieValue.length}, browsers will refuse it`); } const existingSetCookie = (0, _arrayPrototype.default)([res.getHeader("set-cookie") || []]); res.setHeader("set-cookie", [...existingSetCookie, cookieValue]); return cookieValue; }, destroy() { store.clear(); const cookieValue = _cookie.default.serialize(cookieName, "", { ...cookieOptions, maxAge: 0 }); const existingSetCookie = (0, _arrayPrototype.default)([res.getHeader("set-cookie") || []]); res.setHeader("set-cookie", [...existingSetCookie, cookieValue]); } }; req.session = session; } function withIronSession(withIronSessionWrapperHandler, { ttl = 15 * 24 * 3600, cookieName = throwOnNoCookieName(), password = throwOnNoPassword(), cookieOptions = {} }) { return async function withIronSessionHandler(...args) { const handlerType = args[0] && args[1] ? "api" : "ssr"; const req = handlerType === "api" ? args[0] : args[0].req; const res = handlerType === "api" ? args[1] : args[0].res; await applySession(req, res, { ttl, cookieName, password, cookieOptions }); return withIronSessionWrapperHandler(...args); }; } function ironSession({ ttl = 15 * 24 * 3600, cookieName = throwOnNoCookieName(), password = throwOnNoPassword(), cookieOptions = {} }) { return function (req, res, next) { applySession(req, res, { ttl, cookieName, password, cookieOptions }).then(() => { next(); }).catch(next); }; } async function getOrCreateStore({ sealed, password, ttl }) { try { return await (0, _ironStore.default)({ sealed, password, ttl }); } catch (err) { if (err.message === "Expired seal" || err.message === "Bad hmac value" || err.message === "Cannot find password: ") { debug("Received error from Iron: %s, session was automatically restarted", err.message); // if seal expires or // if seal is not valid (encrypted using a different password, when passwords are updated) or // if we can't find back the password in the seal // then we just start a new session over return await (0, _ironStore.default)({ password, ttl }); } throw err; } } //# sourceMappingURL=index.js.map