@qelos/auth
Version:
Express Passport authentication service
235 lines (234 loc) • 8.91 kB
JavaScript
;
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;