mongodb-dynamic-api
Version:
Auto generated CRUD API for MongoDB using NestJS
184 lines • 8.23 kB
JavaScript
"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