@r1tsu/payload
Version:
118 lines (117 loc) • 4.99 kB
JavaScript
import crypto from 'crypto';
import httpStatus from 'http-status';
import { URL } from 'url';
import { buildAfterOperation } from '../../collections/operations/utils.js';
import { APIError } from '../../errors/index.js';
import { commitTransaction } from '../../utilities/commitTransaction.js';
import { initTransaction } from '../../utilities/initTransaction.js';
import { killTransaction } from '../../utilities/killTransaction.js';
export const forgotPasswordOperation = async (incomingArgs)=>{
if (!Object.prototype.hasOwnProperty.call(incomingArgs.data, 'email')) {
throw new APIError('Missing email.', httpStatus.BAD_REQUEST);
}
let args = incomingArgs;
try {
const shouldCommit = await initTransaction(args.req);
// /////////////////////////////////////
// beforeOperation - Collection
// /////////////////////////////////////
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook)=>{
await priorHook;
args = await hook({
args,
collection: args.collection?.config,
context: args.req.context,
operation: 'forgotPassword',
req: args.req
}) || args;
}, Promise.resolve());
const { collection: { config: collectionConfig }, data, disableEmail, expiration, req: { payload: { config, email }, payload }, req } = args;
// /////////////////////////////////////
// Forget password
// /////////////////////////////////////
let token = crypto.randomBytes(20).toString('hex');
if (!data.email) {
throw new APIError('Missing email.', httpStatus.BAD_REQUEST);
}
let user = await payload.db.findOne({
collection: collectionConfig.slug,
req,
where: {
email: {
equals: data.email.toLowerCase()
}
}
});
// We don't want to indicate specifically that an email was not found,
// as doing so could lead to the exposure of registered emails.
// Therefore, we prefer to fail silently.
if (!user) return null;
user.resetPasswordToken = token;
user.resetPasswordExpiration = new Date(expiration || Date.now() + 3600000).toISOString() // 1 hour
;
user = await payload.update({
id: user.id,
collection: collectionConfig.slug,
data: user,
req
});
if (!disableEmail) {
const protocol = new URL(req.url).protocol;
const serverURL = config.serverURL !== null && config.serverURL !== '' ? config.serverURL : `${protocol}://${req.headers.get('host')}`;
let html = `${req.t('authentication:youAreReceivingResetPassword')}
<a href="${serverURL}${config.routes.admin}/reset/${token}">
${serverURL}${config.routes.admin}/reset/${token}
</a>
${req.t('authentication:youDidNotRequestPassword')}`;
if (typeof collectionConfig.auth.forgotPassword.generateEmailHTML === 'function') {
html = await collectionConfig.auth.forgotPassword.generateEmailHTML({
req,
token,
user
});
}
let subject = req.t('authentication:resetYourPassword');
if (typeof collectionConfig.auth.forgotPassword.generateEmailSubject === 'function') {
subject = await collectionConfig.auth.forgotPassword.generateEmailSubject({
req,
token,
user
});
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
email.sendEmail({
from: `"${email.defaultFromName}" <${email.defaultFromAddress}>`,
html,
subject,
to: data.email
});
}
// /////////////////////////////////////
// afterForgotPassword - Collection
// /////////////////////////////////////
await collectionConfig.hooks.afterForgotPassword.reduce(async (priorHook, hook)=>{
await priorHook;
await hook({
args,
collection: args.collection?.config,
context: req.context
});
}, Promise.resolve());
// /////////////////////////////////////
// afterOperation - Collection
// /////////////////////////////////////
token = await buildAfterOperation({
args,
collection: args.collection?.config,
operation: 'forgotPassword',
result: token
});
if (shouldCommit) await commitTransaction(req);
return token;
} catch (error) {
await killTransaction(args.req);
throw error;
}
};
//# sourceMappingURL=forgotPassword.js.map