@snowtop/ent-passport
Version:
snowtop ent passport integration
250 lines (249 loc) • 9.3 kB
JavaScript
;
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;