UNPKG

@snowtop/ent-passport

Version:

snowtop ent passport integration

250 lines (249 loc) 9.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.useAndVerifyAuthJWT = exports.defaultViewerToPayload = exports.useAndVerifyAuth = exports.useAndAuth = exports.LocalStrategy = exports.PassportStrategyHandler = exports.PassportAuthHandler = void 0; const passport_1 = __importDefault(require("passport")); const auth_1 = require("@snowtop/ent/auth"); const passport_strategy_1 = require("passport-strategy"); const ent_1 = require("@snowtop/ent"); const passport_jwt_1 = require("passport-jwt"); const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const express_session_1 = __importDefault(require("express-session")); // should this be renamed to session? class PassportAuthHandler { constructor(options) { this.options = options; } async authViewer(context) { var _a; let that = this; passport_1.default.serializeUser(function (viewer, done) { var _a; let serializeUser = (_a = that.options) === null || _a === void 0 ? void 0 : _a.serializeViewer; if (!serializeUser) { serializeUser = (viewer) => { return viewer.viewerID; }; } done(null, serializeUser(viewer)); }); passport_1.default.deserializeUser(function (id, done) { var _a; let deserializeUser = (_a = that.options) === null || _a === void 0 ? void 0 : _a.deserializeViewer; if (!deserializeUser) { deserializeUser = async (id) => { var _a; if ((_a = that.options) === null || _a === void 0 ? void 0 : _a.loadOptions) { let ent = await (0, ent_1.loadEntX)(new ent_1.IDViewer(id, { context }), id, that.options.loadOptions); return new ent_1.IDViewer(id, { context, ent }); } return new ent_1.IDViewer({ viewerID: id, context }); }; } done(null, deserializeUser(id)); }); let user = context.request["user"]; if (!user) { return null; } let userr = await user; return await toViewer(context, userr, (_a = this.options) === null || _a === void 0 ? void 0 : _a.userToViewer); } static testInitSessionBasedFunction(secret, options) { return (app) => { app.use((0, express_session_1.default)({ secret: secret, })); app.use(passport_1.default.initialize()); app.use(passport_1.default.session()); (0, auth_1.registerAuthHandler)("viewer", new PassportAuthHandler(options)); }; } } exports.PassportAuthHandler = PassportAuthHandler; async function toViewer(context, obj, userToViewer) { if (obj.viewerID !== undefined) { return obj; } if (userToViewer) { return await userToViewer(context, obj); } throw new Error("cannot convert to Viewer"); } // passportstrategyhandler // to be used for other requests when JWT is passed function defaultReqToViewer(loadOptions) { return async (context, viewerID) => { let ent = null; if (loadOptions) { ent = await (0, ent_1.loadEnt)(new ent_1.IDViewer(viewerID, { context }), viewerID, loadOptions); } return new ent_1.IDViewer(viewerID, { context, ent: ent }); }; } class PassportStrategyHandler { constructor(strategy, options, reqToViewer) { this.strategy = strategy; this.options = options; this.reqToViewer = reqToViewer; if (!this.strategy.name) { throw new Error("name required for strategy"); } passport_1.default.use(strategy); } async authViewer(context) { // console.log("passport authViewer", context.getViewer()); const viewerMaybe = await promisifiedAuth(context, this.strategy, this.options); if (!viewerMaybe) { return null; } const viewer = await toViewer(context, viewerMaybe, this.reqToViewer); // needed to pass viewer to auth await context.authViewer(viewer); return viewer; } static jwtHandler(opts) { let strategyOpts; if (opts.strategyOpts) { strategyOpts = opts.strategyOpts; } else { if (!opts.secretOrKey) { throw new Error(`must provide secretOrKey if strategyOpts not proivded`); } strategyOpts = { secretOrKey: opts.secretOrKey, jwtFromRequest: opts.jwtFromRequest || passport_jwt_1.ExtractJwt.fromAuthHeaderAsBearerToken(), }; } let reqToViewer = opts.reqToViewer || defaultReqToViewer(opts.loaderOptions); return new PassportStrategyHandler(new passport_jwt_1.Strategy(strategyOpts, function (jwt_payload, next) { return next(null, jwt_payload["viewerID"].toString(), {}); }), opts.authOptions, reqToViewer); } static testInitJWTFunction(opts) { return (app) => { app.use(passport_1.default.initialize()); (0, auth_1.registerAuthHandler)("viewer", PassportStrategyHandler.jwtHandler(opts)); }; } } exports.PassportStrategyHandler = PassportStrategyHandler; class LocalStrategy extends passport_strategy_1.Strategy { constructor(options) { super(); this.options = options; this.name = "ent-local"; } async authenticate(_req) { //console.log("local strategy authenticate called"); let viewer = await this.options.verifyFn(); //console.log("auth viewer", viewer); // we actually want the logged in viewer here if (viewer) { this.success(viewer); return viewer; } else { this.fail(401); return null; } } } exports.LocalStrategy = LocalStrategy; function promisifiedAuth(context, strategy, options) { return new Promise((resolve, reject) => { const done = (err, user, _info) => { //console.log("done", err, user); if (err) { reject(err); } else { resolve(user); } }; options = options || {}; let authMethod = passport_1.default.authenticate(strategy.name, options, done); return authMethod(context.request, context.response, (err) => { console.error("err", err); }); }); } function promisifiedLogin(context, viewer, options) { if (typeof context.request["login"] !== "function") { return null; } return new Promise((resolve, reject) => { const done = (err) => { if (err) { reject(err); } else { resolve(); } }; // log the user in! // call the login function // need to call it with request as this context.request["login"](viewer, options, done); }); } async function useAndAuth(context, strategy, options) { if (!strategy.name) { throw new Error("name required for strategy"); } passport_1.default.use(strategy); let viewer = await promisifiedAuth(context, strategy, options); if (!viewer) { return viewer; } // auth the viewer with context await context.authViewer(viewer); // login the user to passport await promisifiedLogin(context, viewer, options); // console.log("useAndAuth", viewer); return viewer; } exports.useAndAuth = useAndAuth; async function useAndVerifyAuth(context, verifyFn, loadOptions, options) { const strategy = new LocalStrategy({ verifyFn: async (ctx) => { const viewerMaybe = await verifyFn(); if (!viewerMaybe) { return null; } return await toViewer(context, viewerMaybe, defaultReqToViewer(loadOptions)); }, }); return await useAndAuth(context, strategy, options); } exports.useAndVerifyAuth = useAndVerifyAuth; function defaultViewerToPayload(viewer) { if (!viewer.viewerID) { return {}; } return { viewerID: viewer.viewerID.toString() }; } exports.defaultViewerToPayload = defaultViewerToPayload; async function useAndVerifyAuthJWT(context, verifyFn, jwtOptions, loadOptions, options) { const viewer = await useAndVerifyAuth(context, verifyFn, loadOptions, options); if (!viewer || !viewer.viewerID) { throw new Error(`invalid login credentials`); } let payload; if (jwtOptions.viewerToPayload) { payload = jwtOptions.viewerToPayload(viewer); } else { payload = defaultViewerToPayload(viewer); } const token = jsonwebtoken_1.default.sign(payload, jwtOptions.secretOrKey, { // may eventually want to provide way to customize subject but not right now subject: viewer.viewerID.toString(), ...jwtOptions.signInOptions, }); return [viewer, token]; } exports.useAndVerifyAuthJWT = useAndVerifyAuthJWT;