UNPKG

@zeushq/nextjs-zidentity

Version:

Next.js SDK for signin in with Zeus Identity

205 lines 9.53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var assert_1 = require("assert"); var jose_1 = require("jose"); var hkdf_1 = require("./utils/hkdf"); var debug_1 = (0, tslib_1.__importDefault)(require("./utils/debug")); var cookies_1 = require("./utils/cookies"); var cookie_1 = require("cookie"); var debug = (0, debug_1.default)('cookie-store'); var epoch = function () { return (Date.now() / 1000) | 0; }; // eslint-disable-line no-bitwise var MAX_COOKIE_SIZE = 4096; var alg = 'dir'; var enc = 'A256GCM'; var notNull = function (value) { return value !== null; }; var CookieStore = /** @class */ (function () { function CookieStore(config) { var _this = this; this.config = config; var secrets = Array.isArray(config.secret) ? config.secret : [config.secret]; this.keystore = new jose_1.JWKS.KeyStore(); secrets.forEach(function (secretString, i) { var key = jose_1.JWK.asKey((0, hkdf_1.encryption)(secretString)); if (i === 0) { _this.currentKey = key; } _this.keystore.add(key); }); var _a = this.config.session, _b = _a.cookie, transient = _b.transient, cookieConfig = (0, tslib_1.__rest)(_b, ["transient"]), sessionName = _a.name; var cookieOptions = (0, tslib_1.__assign)({}, cookieConfig); if (!transient) { cookieOptions.expires = new Date(); } var emptyCookie = (0, cookie_1.serialize)(sessionName + ".0", '', cookieOptions); this.chunkSize = MAX_COOKIE_SIZE - emptyCookie.length; } CookieStore.prototype.encrypt = function (payload, headers) { return jose_1.JWE.encrypt(payload, this.currentKey, (0, tslib_1.__assign)({ alg: alg, enc: enc }, headers)); }; CookieStore.prototype.decrypt = function (jwe) { return jose_1.JWE.decrypt(jwe, this.keystore, { complete: true, contentEncryptionAlgorithms: [enc], keyManagementAlgorithms: [alg] }); }; CookieStore.prototype.calculateExp = function (iat, uat) { var absoluteDuration = this.config.session.absoluteDuration; var _a = this.config.session, rolling = _a.rolling, rollingDuration = _a.rollingDuration; if (typeof absoluteDuration !== 'number') { return uat + rollingDuration; } if (!rolling) { return iat + absoluteDuration; } return Math.min(uat + rollingDuration, iat + absoluteDuration); }; CookieStore.prototype.read = function (req) { var _a; var cookies = (0, cookies_1.getAll)(req); var _b = this.config.session, sessionName = _b.name, rollingDuration = _b.rollingDuration, absoluteDuration = _b.absoluteDuration; var iat; var uat; var exp; var existingSessionValue; try { if (sessionName in cookies) { // get JWE from unchunked session cookie debug('reading session from %s cookie', sessionName); existingSessionValue = cookies[sessionName]; } else if (sessionName + ".0" in cookies) { // get JWE from chunked session cookie // iterate all cookie names // match and filter for the ones that match sessionName.<number> // sort by chunk index // concat existingSessionValue = Object.entries(cookies) .map(function (_a) { var _b = (0, tslib_1.__read)(_a, 2), cookie = _b[0], value = _b[1]; var match = cookie.match("^" + sessionName + "\\.(\\d+)$"); if (match) { return [match[1], value]; } return null; }) .filter(notNull) .sort(function (_a, _b) { var _c = (0, tslib_1.__read)(_a, 1), a = _c[0]; var _d = (0, tslib_1.__read)(_b, 1), b = _d[0]; return parseInt(a, 10) - parseInt(b, 10); }) .map(function (_a) { var _b = (0, tslib_1.__read)(_a, 2), i = _b[0], chunk = _b[1]; debug('reading session chunk from %s.%d cookie', sessionName, i); return chunk; }) .join(''); } if (existingSessionValue) { var _c = this.decrypt(existingSessionValue), header = _c.protected, cleartext = _c.cleartext; (_a = header, iat = _a.iat, uat = _a.uat, exp = _a.exp); // check that the existing session isn't expired based on options when it was established (0, assert_1.strict)(exp > epoch(), 'it is expired based on options when it was established'); // check that the existing session isn't expired based on current rollingDuration rules if (rollingDuration) { (0, assert_1.strict)(uat + rollingDuration > epoch(), 'it is expired based on current rollingDuration rules'); } // check that the existing session isn't expired based on current absoluteDuration rules if (typeof absoluteDuration === 'number') { (0, assert_1.strict)(iat + absoluteDuration > epoch(), 'it is expired based on current absoluteDuration rules'); } return [JSON.parse(cleartext.toString()), iat]; } } catch (err) { /* istanbul ignore else */ if (err instanceof assert_1.AssertionError) { debug('existing session was rejected because', err.message); } else if (err instanceof jose_1.errors.JOSEError) { debug('existing session was rejected because it could not be decrypted', err); } else { debug('unexpected error handling session', err); } } return []; }; CookieStore.prototype.save = function (req, res, session, createdAt) { var e_1, _a, e_2, _b; var _c = this.config.session, _d = _c.cookie, transient = _d.transient, cookieConfig = (0, tslib_1.__rest)(_d, ["transient"]), sessionName = _c.name; var cookies = (0, cookies_1.getAll)(req); if (!session) { debug('clearing all matching session cookies'); try { for (var _e = (0, tslib_1.__values)(Object.keys(cookies)), _f = _e.next(); !_f.done; _f = _e.next()) { var cookieName = _f.value; if (cookieName.match("^" + sessionName + "(?:\\.\\d)?$")) { (0, cookies_1.clear)(res, cookieName, { domain: cookieConfig.domain, path: cookieConfig.path }); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_f && !_f.done && (_a = _e.return)) _a.call(_e); } finally { if (e_1) throw e_1.error; } } return; } var uat = epoch(); var iat = typeof createdAt === 'number' ? createdAt : uat; var exp = this.calculateExp(iat, uat); var cookieOptions = (0, tslib_1.__assign)({}, cookieConfig); if (!transient) { cookieOptions.expires = new Date(exp * 1000); } debug('found session, creating signed session cookie(s) with name %o(.i)', sessionName); var value = this.encrypt(JSON.stringify(session), { iat: iat, uat: uat, exp: exp }); var chunkCount = Math.ceil(value.length / this.chunkSize); if (chunkCount > 1) { debug('cookie size greater than %d, chunking', this.chunkSize); for (var i = 0; i < chunkCount; i++) { var chunkValue = value.slice(i * this.chunkSize, (i + 1) * this.chunkSize); var chunkCookieName = sessionName + "." + i; (0, cookies_1.set)(res, chunkCookieName, chunkValue, cookieOptions); } if (sessionName in cookies) { (0, cookies_1.clear)(res, sessionName, { domain: cookieConfig.domain, path: cookieConfig.path }); } } else { (0, cookies_1.set)(res, sessionName, value, cookieOptions); try { for (var _g = (0, tslib_1.__values)(Object.keys(cookies)), _h = _g.next(); !_h.done; _h = _g.next()) { var cookieName = _h.value; if (cookieName.match("^" + sessionName + "\\.\\d$")) { (0, cookies_1.clear)(res, cookieName, { domain: cookieConfig.domain, path: cookieConfig.path }); } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_h && !_h.done && (_b = _g.return)) _b.call(_g); } finally { if (e_2) throw e_2.error; } } } }; return CookieStore; }()); exports.default = CookieStore; //# sourceMappingURL=cookie-store.js.map