UNPKG

@qelos/auth

Version:

Express Passport authentication service

235 lines (234 loc) 8.91 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; 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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const mongoose_1 = __importStar(require("mongoose")); const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const bcryptjs_1 = __importDefault(require("bcryptjs")); const tokens_1 = require("../services/tokens"); const cache_manager_1 = require("../services/cache-manager"); const config_1 = require("../../config"); const TokenSchema = new mongoose_1.default.Schema({ kind: { type: String, enum: ['cookie', 'oauth'], default: () => config_1.defaultAuthType, }, metadata: { type: mongoose_1.default.Schema.Types.Mixed, default: () => ({}), }, tokenIdentifier: String, expiresAt: Date, }); // define the User model schema const UserSchema = new mongoose_1.default.Schema({ tenant: { type: String, index: true, default: '0', }, username: { type: String, required: true, }, email: { type: String, validate(email = '') { return !email || email.includes('@'); } }, emailVerified: { type: Boolean, default: () => false, }, socialLogins: [String], phone: String, password: String, fullName: String, firstName: String, lastName: String, birthDate: String, profileImage: String, salt: String, roles: { type: [String], }, metadata: mongoose_1.default.Schema.Types.Mixed, tokens: [TokenSchema], lastLogin: { created: Date, workspace: { type: mongoose_1.Schema.Types.ObjectId, ref: 'Workspace' } }, created: { type: Date, default: Date.now, }, }); UserSchema.index({ tenant: 1, username: 1 }, { unique: true }); /** * Compare the passed password with the value in the database. A model method. * * @param {string} password * @param {function} callback * @returns {object} callback */ UserSchema.methods.comparePassword = function comparePassword(password, callback) { bcryptjs_1.default.compare(password, this.password, callback); }; UserSchema.methods.getToken = function getToken({ authType, expiresIn, workspace }) { let tokenIdentifier; if (authType === 'cookie') { tokenIdentifier = (0, tokens_1.getUniqueId)(); this.tokens.push({ expiresAt: new Date(Date.now() + config_1.cookieTokenExpiration), kind: authType, tokenIdentifier, }); this.markModified('tokens'); } this.lastLogin = { created: new Date(), workspace: workspace === null || workspace === void 0 ? void 0 : workspace._id }; return (0, tokens_1.getSignedToken)(this, workspace, tokenIdentifier, expiresIn).token; }; UserSchema.methods.getRefreshToken = function getRefreshToken(relatedToken, workspace) { const tokenIdentifier = (0, tokens_1.getUniqueId)(); this.tokens.push({ kind: 'oauth', tokenIdentifier, metadata: { relatedToken, workspace: workspace === null || workspace === void 0 ? void 0 : workspace._id }, expiresAt: new Date(Date.now() + config_1.cookieTokenExpiration), }); this.markModified('tokens'); return jsonwebtoken_1.default.sign({ sub: this._id, tenant: this.tenant, workspace: workspace === null || workspace === void 0 ? void 0 : workspace._id, tokenIdentifier, }, config_1.refreshTokenSecret, { expiresIn: config_1.refreshTokenExpiration }); }; UserSchema.methods.updateToken = function updateToken(authType, currentPayload, newIdentifier, relatedToken) { var _a, _b; this.tokens = this.tokens.filter((token) => !(token.kind === authType && token.tokenIdentifier === currentPayload.tokenIdentifier)); const token = { kind: authType, tokenIdentifier: newIdentifier }; if (relatedToken || currentPayload.workspace) { token.metadata = { relatedToken, workspace: (_a = currentPayload.workspace) === null || _a === void 0 ? void 0 : _a._id }; } this.tokens.push(token); this.lastLogin = { created: new Date(), workspace: (_b = currentPayload.workspace) === null || _b === void 0 ? void 0 : _b._id }; return this.save(); }; UserSchema.methods.deleteToken = function deleteToken(authType, tokenIdentifier) { this.tokens = this.tokens.filter((token) => !(token.kind === authType && token.tokenIdentifier === tokenIdentifier)); return this.save(); }; UserSchema.methods.getTokenByRelatedTokens = function getTokenByRelatedTokens(authType, tokenIdentifier) { const token = this.tokens.find((token) => { var _a; return token.kind === authType && ((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.relatedToken) === tokenIdentifier; }); return token ? token.tokenIdentifier : tokenIdentifier; }; UserSchema.statics.getUsersList = function getUsersList(tenant, usersIds, privilegedUserFields) { if (!usersIds.length) { return this.find({ tenant }) .select(privilegedUserFields) .lean() .exec() .then(users => JSON.stringify(users)); } return cache_manager_1.cacheManager.wrap(`usersList.${tenant}.${usersIds.map(id => id.toString()).join(',')}`, () => __awaiter(this, void 0, void 0, function* () { const query = { _id: { $in: usersIds } }; query.tenant = tenant; try { const users = yield this.find(query) .select(privilegedUserFields) .lean() .exec(); return JSON.stringify(users); } catch (_a) { return '[]'; } })); }; /** * The pre-save hook method. */ UserSchema.pre('save', function saveHook(next) { const user = this; if (user.email && !user.username) { user.username = user.email; } if (!user.email) { user.email = user.username.includes('@') ? user.username : (user.username + '@null'); } // define role for new user if (!user.roles || user.roles.length === 0) { user.roles = [config_1.defaultRole]; } if (user.tokens.length > 10) { const now = Date.now(); user.tokens = user.tokens.filter(token => token.expiresAt && token.expiresAt - now > 0); } if (!this.salt) { this.salt = bcryptjs_1.default.genSaltSync(); } // proceed further only if the password is modified or the user is new if (!user.isModified('password')) return next(); return bcryptjs_1.default.genSalt((saltError, salt) => { if (saltError) { return next(saltError); } return bcryptjs_1.default.hash(user.password, salt, (hashError, hash) => { if (hashError) { return next(hashError); } // replace a password string with hash value user.password = hash; return next(); }); }); }); const User = mongoose_1.default.model('User', UserSchema); exports.default = User;