227 lines
5.6 KiB
JavaScript
227 lines
5.6 KiB
JavaScript
const UsersDBApi = require('../db/api/users');
|
|
const ValidationError = require('./notifications/errors/validation');
|
|
const ForbiddenError = require('./notifications/errors/forbidden');
|
|
const bcrypt = require('bcrypt');
|
|
const EmailAddressVerificationEmail = require('./email/list/addressVerification');
|
|
const InvitationEmail = require('./email/list/invitation');
|
|
const PasswordResetEmail = require('./email/list/passwordReset');
|
|
const EmailSender = require('./email');
|
|
const config = require('../config');
|
|
const helpers = require('../helpers');
|
|
|
|
class Auth {
|
|
static async signup(email, password, options = {}, host) {
|
|
const user = await UsersDBApi.findBy({ email });
|
|
|
|
const hashedPassword = await bcrypt.hash(
|
|
password,
|
|
config.bcrypt.saltRounds,
|
|
);
|
|
|
|
if (user) {
|
|
if (user.authenticationUid) {
|
|
throw new ValidationError('auth.emailAlreadyInUse');
|
|
}
|
|
|
|
if (user.disabled) {
|
|
throw new ValidationError('auth.userDisabled');
|
|
}
|
|
|
|
await UsersDBApi.updatePassword(user.id, hashedPassword, options);
|
|
|
|
if (EmailSender.isConfigured) {
|
|
await this.sendEmailAddressVerificationEmail(user.email, host);
|
|
}
|
|
|
|
const data = {
|
|
user: {
|
|
id: user.id,
|
|
email: user.email,
|
|
},
|
|
};
|
|
|
|
return helpers.jwtSign(data);
|
|
}
|
|
|
|
const newUser = await UsersDBApi.createFromAuth(
|
|
{
|
|
firstName: email.split('@')[0],
|
|
password: hashedPassword,
|
|
email: email,
|
|
},
|
|
options,
|
|
);
|
|
|
|
if (EmailSender.isConfigured) {
|
|
await this.sendEmailAddressVerificationEmail(newUser.email, host);
|
|
}
|
|
|
|
const data = {
|
|
user: {
|
|
id: newUser.id,
|
|
email: newUser.email,
|
|
},
|
|
};
|
|
|
|
return helpers.jwtSign(data);
|
|
}
|
|
|
|
static async signin(email, password, options = {}) {
|
|
const user = await UsersDBApi.findBy({ email });
|
|
|
|
if (!user) {
|
|
throw new ValidationError('auth.userNotFound');
|
|
}
|
|
|
|
if (user.disabled) {
|
|
throw new ValidationError('auth.userDisabled');
|
|
}
|
|
|
|
if (!user.password) {
|
|
throw new ValidationError('auth.wrongPassword');
|
|
}
|
|
|
|
if (!EmailSender.isConfigured) {
|
|
user.emailVerified = true;
|
|
}
|
|
|
|
if (!user.emailVerified) {
|
|
throw new ValidationError('auth.userNotVerified');
|
|
}
|
|
|
|
const passwordsMatch = await bcrypt.compare(password, user.password);
|
|
|
|
if (!passwordsMatch) {
|
|
throw new ValidationError('auth.wrongPassword');
|
|
}
|
|
|
|
const data = {
|
|
user: {
|
|
id: user.id,
|
|
email: user.email,
|
|
},
|
|
};
|
|
|
|
return helpers.jwtSign(data);
|
|
}
|
|
|
|
static async sendEmailAddressVerificationEmail(email, host) {
|
|
let link;
|
|
try {
|
|
const token = await UsersDBApi.generateEmailVerificationToken(email);
|
|
link = `${host}/verify-email?token=${token}`;
|
|
} catch (error) {
|
|
console.error(error);
|
|
throw new ValidationError('auth.emailAddressVerificationEmail.error');
|
|
}
|
|
|
|
const emailAddressVerificationEmail = new EmailAddressVerificationEmail(
|
|
email,
|
|
link,
|
|
);
|
|
|
|
return new EmailSender(emailAddressVerificationEmail).send();
|
|
}
|
|
|
|
static async sendPasswordResetEmail(email, type = 'register', host) {
|
|
let link;
|
|
|
|
try {
|
|
const token = await UsersDBApi.generatePasswordResetToken(email);
|
|
link = `${host}/password-reset?token=${token}`;
|
|
} catch (error) {
|
|
console.error(error);
|
|
throw new ValidationError('auth.passwordReset.error');
|
|
}
|
|
|
|
let passwordResetEmail;
|
|
if (type === 'register') {
|
|
passwordResetEmail = new PasswordResetEmail(email, link);
|
|
}
|
|
if (type === 'invitation') {
|
|
passwordResetEmail = new InvitationEmail(email, link);
|
|
}
|
|
|
|
return new EmailSender(passwordResetEmail).send();
|
|
}
|
|
|
|
static async verifyEmail(token, options = {}) {
|
|
const user = await UsersDBApi.findByEmailVerificationToken(token, options);
|
|
|
|
if (!user) {
|
|
throw new ValidationError(
|
|
'auth.emailAddressVerificationEmail.invalidToken',
|
|
);
|
|
}
|
|
|
|
return UsersDBApi.markEmailVerified(user.id, options);
|
|
}
|
|
|
|
static async passwordUpdate(currentPassword, newPassword, options) {
|
|
const currentUser = options.currentUser || null;
|
|
if (!currentUser) {
|
|
throw new ForbiddenError();
|
|
}
|
|
|
|
const currentPasswordMatch = await bcrypt.compare(
|
|
currentPassword,
|
|
currentUser.password,
|
|
);
|
|
|
|
if (!currentPasswordMatch) {
|
|
throw new ValidationError('auth.wrongPassword');
|
|
}
|
|
|
|
const newPasswordMatch = await bcrypt.compare(
|
|
newPassword,
|
|
currentUser.password,
|
|
);
|
|
|
|
if (newPasswordMatch) {
|
|
throw new ValidationError('auth.passwordUpdate.samePassword');
|
|
}
|
|
|
|
const hashedPassword = await bcrypt.hash(
|
|
newPassword,
|
|
config.bcrypt.saltRounds,
|
|
);
|
|
|
|
return UsersDBApi.updatePassword(currentUser.id, hashedPassword, options);
|
|
}
|
|
|
|
static async passwordReset(token, password, options = {}) {
|
|
const user = await UsersDBApi.findByPasswordResetToken(token, options);
|
|
|
|
if (!user) {
|
|
throw new ValidationError('auth.passwordReset.invalidToken');
|
|
}
|
|
|
|
const hashedPassword = await bcrypt.hash(
|
|
password,
|
|
config.bcrypt.saltRounds,
|
|
);
|
|
|
|
return UsersDBApi.updatePassword(user.id, hashedPassword, options);
|
|
}
|
|
|
|
static async updateProfile(data, currentUser) {
|
|
let transaction = await db.sequelize.transaction();
|
|
|
|
try {
|
|
await UsersDBApi.findBy({ id: currentUser.id }, { transaction });
|
|
|
|
await UsersDBApi.update(currentUser.id, data, {
|
|
currentUser,
|
|
transaction,
|
|
});
|
|
|
|
await transaction.commit();
|
|
} catch (error) {
|
|
await transaction.rollback();
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = Auth;
|