UNPKG

@asfweb/grpc-session

Version:

## Installation: ``` yarn add @asfweb/grpc-session ``` or ``` npm install @asfweb/grpc-session --save ```

331 lines (330 loc) 13 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Session = exports.SessionError = void 0; // Session var grpc_js_1 = require("@grpc/grpc-js"); var crypto_js_1 = require("crypto-js"); var cookie_1 = __importDefault(require("cookie")); var moment_1 = __importDefault(require("moment")); var nanoid_1 = require("nanoid"); var constants_1 = require("./constants"); // import { SessionRedisStore } from "."; /** * Session Error Class */ var SessionError = /** @class */ (function (_super) { __extends(SessionError, _super); function SessionError(message) { var _newTarget = this.constructor; var _this = // 'Error' breaks prototype chain here _super.call(this, message) || this; // Restore prototype chain var proto = _newTarget.prototype; if (Object.setPrototypeOf) { Object.setPrototypeOf(_this, proto); } else { _this.__proto__ = proto; } return _this; } return SessionError; }(Error)); exports.SessionError = SessionError; /** * Session class */ var Session = /** @class */ (function () { /** * Session * * @param store Session Store * @param options {sessionName:"_SID", expires: "Time in seconds: 60*60*20"} */ function Session(store, options) { if (options === void 0) { options = { sessionName: "_SID", checkOrigin: true, expires: 60 * 60 * 20, cookie: { path: "/", httpOnly: true }, debug: false, }; } this.sessionData = null; this.store = store; this.sessionId = ""; this.sessionName = options.sessionName || "_SID"; this.options = options; this.metadata = new grpc_js_1.Metadata(); } /** * Creates new session * * @param sessionData * @returns Session */ Session.prototype.start = function (sessionData) { this.sessionId = (0, nanoid_1.nanoid)(); this.sessionData = {}; if (sessionData) { this.sessionData = sessionData; } return this; }; /** * Will try to restore session from id or will create a new one * * @param call ServerSurfaceCall * @returns Promise<Session> */ Session.prototype.gRPC = function (call, sessionData) { return __awaiter(this, void 0, void 0, function () { var cookiesHeader, cookies, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: // Check cookies for session id this.metadata = call.metadata; cookiesHeader = this.metadata.get("cookie").toString(); cookies = cookie_1.default.parse(cookiesHeader); if (!cookies[this.sessionName]) return [3 /*break*/, 6]; this.sessionId = cookies[this.sessionName]; _a = this; return [4 /*yield*/, this.store.get(this.sessionId)]; case 1: _a.sessionData = _b.sent(); if (!(this.sessionData === null)) return [3 /*break*/, 2]; this.start(); return [3 /*break*/, 5]; case 2: if (!(this.sessionData.exp && this.sessionData.exp < (0, moment_1.default)().unix())) return [3 /*break*/, 4]; if (this.options.debug) { console.log("Start new session: Previous session has been expired."); } return [4 /*yield*/, this.store.delete(this.sessionId)]; case 3: _b.sent(); this.start(); _b.label = 4; case 4: // Check origin if (this.options.checkOrigin && this.get("hash") !== this._MD5_hash()) { if (this.options.debug) { console.log("Start new session: Session has different origin."); } this.start(); } _b.label = 5; case 5: return [3 /*break*/, 7]; case 6: if (this.options.debug) { console.log("Start new session: Session cookie does not exist."); } // SessionId does not exist in cookies header start new session this.start(); _b.label = 7; case 7: // Sends cookie header call.sendMetadata(this.getMetadata()); // Initial session data this.sessionData = __assign(__assign({}, this.sessionData), sessionData); // Save session return [4 /*yield*/, this.save()]; case 8: // Save session _b.sent(); return [2 /*return*/, this]; } }); }); }; /** * Get session key * * @param key string key * @returns string * @throws SessionError */ Session.prototype.get = function (key) { if (this.sessionData === null) { throw new SessionError(constants_1._ERROR_SESSION_DATA); } if (key) { return this.sessionData[key]; } return this.sessionData; }; /** * Sets new session key * * @param key string * @param value string * @returns Session * @throws SessionError */ Session.prototype.set = function (key, value) { if (this.sessionData === null) { throw new SessionError(constants_1._ERROR_SESSION_DATA); } var newData = {}; newData[key] = value; this.sessionData = __assign(__assign({}, this.sessionData), newData); return this; }; /** * Removes key from session * * @param key string * @returns Session * @throws SessionError */ Session.prototype.remove = function (key) { if (this.sessionData === null) { throw new SessionError(constants_1._ERROR_SESSION_DATA); } delete this.sessionData[key]; return this; }; /** * Gets session id * * @returns string * @throws SessionError */ Session.prototype.id = function () { if (!this.sessionId) { throw new SessionError(constants_1._ERROR_SESSION_ID); } return this.sessionId; }; /** * Get Grpc Metadata * * @returns Metadata * @throws SessionError */ Session.prototype.getMetadata = function () { if (this.sessionData === null) { throw new SessionError(constants_1._ERROR_SESSION_DATA); } var defaultCookiesOptions = { path: "/", httpOnly: true, secure: true, SameSite: "Lax", }; var options = __assign(__assign(__assign({}, defaultCookiesOptions), { maxAge: this.options.expires || 0 }), this.options.cookie); var metadata = new grpc_js_1.Metadata(); metadata.set("Set-Cookie", cookie_1.default.serialize(this.sessionName, this.sessionId, options)); return metadata; }; /** * Saves Session * * @returns Promise<boolean> * @throws SessionError */ Session.prototype.save = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { if (this.sessionData === null) { throw new SessionError(constants_1._ERROR_SESSION_DATA); } // Sets expiration time inside session data if (this.options.expires) { this.set("exp", (0, moment_1.default)().unix() + this.options.expires); } if (this.options.checkOrigin) { this.set("hash", this._MD5_hash()); } return [2 /*return*/, this.store.set(this.sessionId, this.sessionData)]; }); }); }; /** * Deletes Session * * @returns Promise<boolean> */ Session.prototype.destroy = function () { return this.store.delete(this.sessionId); }; /** * A md5 hash of sessionId : user-agent header * * @returns string */ Session.prototype._MD5_hash = function () { var userAgent = this.metadata.get("user-agent").toString(); var origin = this.metadata.get("origin").toString(); var hash = (0, crypto_js_1.MD5)("".concat(this.sessionId, ":").concat(userAgent, ":").concat(origin)).toString(); return hash; }; return Session; }()); exports.Session = Session;