UNPKG

@krypton-org/krypton-auth

Version:

Express authentication middleware, using GraphQL and JSON Web Tokens.

198 lines 8.14 kB
"use strict"; 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 }; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); /** * Module returning the Mongoose model built with the UserSchema importer from {@link module:Model/UserSchema} * @module model/UserModel */ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const mongoose_1 = require("mongoose"); const config_1 = __importDefault(require("../config")); const PasswordEncryption = __importStar(require("../crypto/PasswordEncryption")); const ErrorTypes_1 = require("../error/ErrorTypes"); const UserSchema_1 = require("./UserSchema"); /** * Compute users JsonWebTokens encoding the `user` data with the `privateKey`. This authentication token will be valid for `expiresIn` number of milliseconds. * @throws {TokenEncryptionError} * @param {any} user * @param {string} privateKey * @param {number} expiresIn * @returns {Promise<string>} Promise to the token. */ const computeUserToken = (user, privateKey, expiresIn) => { return new Promise((resolve, reject) => { jsonwebtoken_1.default.sign(user, privateKey, { expiresIn: expiresIn + 'ms', algorithm: config_1.default.algorithm }, (err, token) => __awaiter(void 0, void 0, void 0, function* () { if (err) { reject(new ErrorTypes_1.TokenEncryptionError(err.message)); } else { resolve(token); } })); }); }; const noninternalFieldsFilter = '-' + UserSchema_1.internalFields.join(' -'); const internalFieldsMap = new Map(); UserSchema_1.internalFields.map(x => internalFieldsMap.set(x, true)); const User = new mongoose_1.Schema(UserSchema_1.UserSchema); /** @see {@link IUserModel#userExists} */ User.statics.userExists = function (filter) { return this.findOne(filter).then(result => !!result); }; /** @see {@link IUserModel#getUser} */ User.statics.getUser = function (filter) { return this.findOne(filter).then(user => { if (user == null) { throw new ErrorTypes_1.UnauthorizedError('User not found.'); } return user; }); }; /** @see {@link IUserModel#getUserNonInternalFields} */ User.statics.getUserNonInternalFields = function (filter) { return this.findOne(filter) .select(noninternalFieldsFilter) .lean() .then(user => { if (user == null) { throw new ErrorTypes_1.UnauthorizedError('User not found.'); } return user; }); }; /** @see {@link IUserModel#createUser} */ User.statics.createUser = function (data) { return __awaiter(this, void 0, void 0, function* () { const UserInstance = mongoose_1.model('user', User); return PasswordEncryption.hashAndSalt(data.password).then(results => { const user = new UserInstance(); Object.keys(data).map(prop => { if (prop !== 'password') { if (internalFieldsMap.has(prop)) { user[prop] = data[prop]; } else { user[prop] = escapeHtml(data[prop]); } } }); user.password = results.hash; user.passwordSalt = results.salt; return user.save(); }, err => { throw new ErrorTypes_1.EncryptionFailedError('Password encryption failed :' + err); }); }); }; /** @see {@link IUserModel#isPasswordValid} */ User.statics.isPasswordValid = function (filter, password) { let user; return this.getUser(filter) .then(userFound => { user = userFound; return PasswordEncryption.hash(password, user.passwordSalt); }) .then(results => { return results.hash === user.password; }); }; /** @see {@link IUserModel#sign} */ User.statics.sign = function (filter, password, privateKey) { return __awaiter(this, void 0, void 0, function* () { const isPasswordValid = yield this.isPasswordValid(filter, password); if (!isPasswordValid) { throw new ErrorTypes_1.UserNotFoundError('Wrong credentials.'); } const user = yield this.getUserNonInternalFields(filter); const expiryDate = new Date(); expiryDate.setTime(expiryDate.getTime() + config_1.default.authTokenExpiryTime); const token = yield computeUserToken(user, privateKey, config_1.default.authTokenExpiryTime); return { user, token, expiryDate }; }); }; /** @see {@link IUserModel#refreshAuthToken} */ User.statics.refreshAuthToken = function (filter, privateKey) { return __awaiter(this, void 0, void 0, function* () { const user = yield this.getUserNonInternalFields(filter); const expiryDate = new Date(); expiryDate.setTime(expiryDate.getTime() + config_1.default.authTokenExpiryTime); const token = yield computeUserToken(user, privateKey, config_1.default.authTokenExpiryTime); return { expiryDate, token, user }; }); }; /** @see {@link IUserModel#verify} */ User.statics.verify = function (token, publicKey) { return new Promise((resolve, reject) => { jsonwebtoken_1.default.verify(token, publicKey, { algorithms: [config_1.default.algorithm] }, (err, userDecrypted) => __awaiter(this, void 0, void 0, function* () { if (err) { reject(new ErrorTypes_1.UnauthorizedError('User not found, please log in.')); } else { resolve(userDecrypted); } })); }); }; /** @see {@link IUserModel#updateUser} */ User.statics.updateUser = function (filter, data) { return __awaiter(this, void 0, void 0, function* () { if (data.password) { try { const results = yield PasswordEncryption.hashAndSalt(data.password); data = Object.assign(Object.assign({}, data), { password: results.hash, passwordSalt: results.salt }); } catch (err) { throw new ErrorTypes_1.EncryptionFailedError('Password encryption failed :' + err); } } Object.keys(data).map(prop => { if (!internalFieldsMap.has(prop)) { data[prop] = escapeHtml(data[prop]); } }); yield this.updateOne(filter, data, { runValidators: true }); }); }; /** @see {@link IUserModel#removeUser} */ User.statics.removeUser = function (filter) { return this.deleteOne(filter); }; /** * Escape unsafe string from HTML injection to protect users from XSS attacks. * @param {string} unsafe * @returns {string} Safe string */ function escapeHtml(unsafe) { if (typeof unsafe !== 'string') { return unsafe; } else { return unsafe .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;'); } } const UserModel = mongoose_1.model('User', User); exports.default = UserModel; //# sourceMappingURL=UserModel.js.map