UNPKG

mongodb-dynamic-api

Version:

Auto generated CRUD API for MongoDB using NestJS

184 lines 8.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseAuthService = void 0; const common_1 = require("@nestjs/common"); const logger_1 = require("../../../logger"); const services_1 = require("../../../services"); class BaseAuthService extends services_1.BaseService { constructor(model, jwtService, bcryptService) { super(model); this.model = model; this.jwtService = jwtService; this.bcryptService = bcryptService; this.loginField = 'email'; this.passwordField = 'password'; this.additionalRequestFields = []; this.logger = new logger_1.MongoDBDynamicApiLogger('AuthService'); } async validateUser(login, pass) { this.verifyArguments(login, pass); const user = (await this.model.findOne({ [this.loginField]: login }).lean().exec()); const isPasswordValid = user ? await this.bcryptService.comparePassword(pass, user[this.passwordField]) : false; if (!user || !isPasswordValid) { return null; } return { ...user, id: user._id.toString() }; } async login(user, fromMember = false) { this.verifyArguments(user); if (!fromMember && !!this.loginCallback) { const fullUser = (await this.model.findOne({ _id: user.id }).lean().exec()); const instance = this.buildInstance(fullUser); await this.loginCallback(instance, this.callbackMethods); } const fieldsToBuild = [ '_id', 'id', this.loginField, ...this.additionalRequestFields, ]; const payload = { ...this.buildUserFields(user, fieldsToBuild), }; return { accessToken: this.jwtService.sign(payload), }; } async register(userToCreate) { this.verifyArguments(userToCreate); this.checkFieldsValidity(userToCreate); try { const hashedPassword = await this.bcryptService.hashPassword(userToCreate[this.passwordField]); if (this.beforeRegisterCallback) { userToCreate = await this.beforeRegisterCallback(userToCreate, { hashedPassword }, this.callbackMethods); } const created = await this.model.create({ ...userToCreate, [this.passwordField]: hashedPassword }); if (this.registerCallback) { const user = (await this.model.findOne({ _id: created._id }).lean().exec()); const instance = this.buildInstance(user); await this.registerCallback(instance, this.callbackMethods); } const user = (await this.model.findOne({ _id: created._id }).lean().exec()); return this.login(user, true); } catch (error) { this.handleDuplicateKeyError(error, false); this.handleMongoErrors(error); } } async getAccount({ id }) { this.verifyArguments(id); const user = (await this.model.findOne({ _id: id }).lean().exec()); const fieldsToBuild = [ '_id', this.loginField, ...this.additionalRequestFields, ]; return this.buildUserFields(user, fieldsToBuild); } async updateAccount({ id }, update) { this.verifyArguments(id, update); if (this.beforeUpdateAccountCallback) { const user = (await this.model.findOne({ _id: id }).lean().exec()); update = await this.beforeUpdateAccountCallback({ ...user, id: user._id.toString() }, { update }, this.callbackMethods); } await this.model.updateOne({ _id: id }, { $set: update }).exec(); if (this.updateAccountCallback) { const fullUser = (await this.model.findOne({ _id: id }).lean().exec()); const instance = this.buildInstance(fullUser); await this.updateAccountCallback(instance, this.callbackMethods); } return this.getAccount({ id }); } async resetPassword(email) { this.verifyArguments(email); if (!this.resetPasswordOptions) { return; } this.resetPasswordCallbackMethods = { findUserByEmail: async () => { const user = await this.model.findOne({ [this.resetPasswordOptions.emailField]: email }) .lean() .exec(); if (!user) { return; } return this.buildInstance(user); }, updateUserByEmail: async (update) => { const user = await this.model.findOneAndUpdate({ [this.resetPasswordOptions.emailField]: email }, update, { new: true }).lean().exec(); if (!user) { return; } return this.buildInstance(user); }, }; const { resetPasswordCallback, expirationInMinutes } = this.resetPasswordOptions; const resetPasswordToken = this.jwtService.sign({ email }, { expiresIn: expirationInMinutes * 60 }); await resetPasswordCallback({ resetPasswordToken, email }, this.resetPasswordCallbackMethods); } async changePassword(resetPasswordToken, newPassword) { this.verifyArguments(resetPasswordToken, newPassword); let email; let exp; try { const decoded = this.jwtService.decode(resetPasswordToken); email = decoded.email; exp = decoded.exp; } catch (error) { this.logger.warn('Invalid reset password token'); } if (!email || !exp) { throw new common_1.BadRequestException('Invalid reset password token. Please redo the reset password process.'); } const now = Math.round(Date.now() / 1000); if (exp <= now) { throw new common_1.UnauthorizedException('Time to reset password has expired. Please redo the reset password process.'); } let userId; try { const { _id } = await this.findOneDocumentWithAbilityPredicate(undefined, { [this.resetPasswordOptions.emailField]: email }, this.resetPasswordOptions?.changePasswordAbilityPredicate); userId = _id.toString(); } catch (error) { if (error.status === 403) { throw new common_1.ForbiddenException('You are not allowed to change your password.'); } this.logger.warn('Invalid email, user not found'); } if (!userId) { return; } const hashedPassword = await this.bcryptService.hashPassword(newPassword); if (this.resetPasswordOptions?.beforeChangePasswordCallback) { const user = (await this.model.findOne({ _id: userId }).lean().exec()); await this.resetPasswordOptions.beforeChangePasswordCallback({ ...user, id: user._id.toString() }, { resetPasswordToken, newPassword, hashedPassword }, this.callbackMethods); } await this.model.updateOne({ _id: userId }, { $set: { [this.passwordField]: hashedPassword, resetPasswordToken: null } }); if (this.resetPasswordOptions?.changePasswordCallback) { const user = (await this.model.findOne({ _id: userId }).lean().exec()); await this.resetPasswordOptions.changePasswordCallback({ ...user, id: user._id.toString() }, this.callbackMethods); } } buildUserFields(user, fieldsToBuild) { return this.buildInstance(fieldsToBuild.reduce((acc, field) => (user[field] !== undefined ? { ...acc, [field]: user[field] } : acc), {})); } checkFieldsValidity(userToCreate) { const errors = []; if (!userToCreate[this.loginField]) { errors.push(`${String(this.loginField)} property is required`); } if (!userToCreate[this.passwordField]) { errors.push(`${String(this.passwordField)} property is required`); } if (!errors.length) { return; } throw new common_1.BadRequestException(errors); } } exports.BaseAuthService = BaseAuthService; //# sourceMappingURL=base-auth.service.js.map