Skip to content

Commit

Permalink
Remove custom password reset email
Browse files Browse the repository at this point in the history
  • Loading branch information
goto-bus-stop committed Jun 30, 2023
1 parent 9eab96b commit 477f8c1
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 73 deletions.
24 changes: 0 additions & 24 deletions src/HttpApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,6 @@ const optionsSchema = JSON.parse(
fs.readFileSync(new URL('./schemas/httpApi.json', import.meta.url), 'utf8'),
);

/**
* @param {{ token: string, requestUrl: string }} options
* @returns {import('nodemailer').SendMailOptions}
*/
function defaultCreatePasswordResetEmail({ token, requestUrl }) {
const parsed = new URL(requestUrl);
const { hostname } = parsed;
const resetLink = new URL(`/reset/${token}`, parsed);
return {
from: `noreply@${hostname}`,
subject: 'üWave Password Reset Request',
text: `
Hello,
To reset your password, please visit:
${resetLink}
`,
};
}

/**
* @typedef {express.Router & { authRegistry: AuthRegistry }} HttpApi
*/
Expand All @@ -62,8 +42,6 @@ function defaultCreatePasswordResetEmail({ token, requestUrl }) {
* @prop {(error: Error) => void} [onError]
* @prop {{ secret: string }} [recaptcha]
* @prop {import('nodemailer').Transport} [mailTransport]
* @prop {(options: { token: string, requestUrl: string }) =>
* import('nodemailer').SendMailOptions} [createPasswordResetEmail]
*
* @typedef {object} HttpApiSettings - Runtime options for the HTTP API.
* @prop {string[]} allowedOrigins
Expand Down Expand Up @@ -120,8 +98,6 @@ async function httpApi(uw, options) {
secret: options.secret,
mailTransport: options.mailTransport,
recaptcha: options.recaptcha,
createPasswordResetEmail:
options.createPasswordResetEmail ?? defaultCreatePasswordResetEmail,
}))
.use('/bans', bans())
.use('/import', imports())
Expand Down
1 change: 0 additions & 1 deletion src/Uwave.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ class UwaveServer extends EventEmitter {
helmet: this.options.helmet,
mailTransport: this.options.mailTransport,
recaptcha: this.options.recaptcha,
createPasswordResetEmail: this.options.createPasswordResetEmail,
onError: this.options.onError,
});
boot.use(SocketServer.plugin);
Expand Down
22 changes: 13 additions & 9 deletions src/controllers/authenticate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import nodeFetch from 'node-fetch';
import ms from 'ms';
import htmlescape from 'htmlescape';
import httpErrors from 'http-errors';
import { createTransport } from 'nodemailer';
import { render } from '@react-email/render';
import {
BannedError,
ReCaptchaError,
InvalidResetTokenError,
UserNotFoundError,
} from '../errors/index.js';
import sendEmail from '../email.js';
import beautifyDuplicateKeyError from '../utils/beautifyDuplicateKeyError.js';
import toItemResponse from '../utils/toItemResponse.js';
import toListResponse from '../utils/toListResponse.js';
import PasswordResetEmail from '../emails/password-reset.js';

const { BadRequest } = httpErrors;

Expand All @@ -24,8 +26,6 @@ const { BadRequest } = httpErrors;
* @prop {string} [origin]
* @prop {import('nodemailer').Transport} [mailTransport]
* @prop {{ secret: string }} [recaptcha]
* @prop {(options: { token: string, requestUrl: string }) =>
* import('nodemailer').SendMailOptions} createPasswordResetEmail
* @prop {boolean} [cookieSecure]
* @prop {string} [cookiePath]
*
Expand Down Expand Up @@ -366,7 +366,7 @@ async function reset(req) {
const uw = req.uwave;
const { Authentication } = uw.models;
const { email } = req.body;
const { mailTransport, createPasswordResetEmail } = req.authOptions;
const { mailTransport } = req.authOptions;

const auth = await Authentication.findOne({
email: email.toLowerCase(),
Expand All @@ -380,14 +380,18 @@ async function reset(req) {
await uw.redis.set(`reset:${token}`, auth.user.toString());
await uw.redis.expire(`reset:${token}`, 24 * 60 * 60);

const message = await createPasswordResetEmail({
const transport = createTransport(mailTransport);
const emailContents = PasswordResetEmail({
token,
requestUrl: req.fullUrl,
publicUrl: new URL(req.fullUrl).origin,
});

await sendEmail(email, {
mailTransport,
email: message,
await transport.sendMail({
to: email,
from: `noreply@${new URL(req.fullUrl).hostname}`,
subject: 'üWave Password Reset Request',
html: render(emailContents),
text: render(emailContents, { plainText: true }),
});

return toItemResponse({});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
} from '@react-email/components';

/**
* @param {{ token?: string }} props
* @param {object} props
* @param {string} props.token
* @param {string} props.publicUrl
*/
export default function PasswordResetEmail({
token = '12c6c577-593a-45d5-861b-4432e4269847',
Expand All @@ -26,7 +28,7 @@ export default function PasswordResetEmail({
return html`
<${Html} lang="en" dir="ltr">
<${Head}>
<title>Password Reset Request</title>
<title>üWave Password Reset Request</title>
<//>
<${Body} style=${bodyStyle}>
<${Container}>
Expand Down
40 changes: 3 additions & 37 deletions test/authenticate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -207,44 +207,10 @@ describe('Password Reset', () => {
sinon.assert.calledWithMatch(sendSpy, {
data: {
to: '[email protected]',
from: sinon.match(/noreply@/),
subject: 'üWave Password Reset Request',
text: sinon.match(/http:\/\/127\.0\.0\.1:\d+\/reset\//),
},
});
});

it('uses a custom email body', async () => {
const sendSpy = sandbox.spy(mailTransport, 'send');
uw = await createUwave('pw_reset', {
mailTransport,
createPasswordResetEmail({ token }) {
assert.strictEqual(typeof token, 'string');
return {
from: '[email protected]',
subject: 'Custom Subject',
text: 'Text body',
html: '<b>HTML body</b>',
};
},
});

const user = await uw.test.createUser();
await uw.models.Authentication.create({
email: '[email protected]',
user,
hash: 'passwordhash',
});

await supertest(uw.server)
.post('/api/auth/password/reset')
.send({ email: '[email protected]' })
.expect(200);

sinon.assert.calledWithMatch(sendSpy, {
data: {
from: '[email protected]',
subject: 'Custom Subject',
text: 'Text body',
html: '<b>HTML body</b>',
html: sinon.match(/http:\/\/127\.0\.0\.1:\d+\/reset\//),
},
});
});
Expand Down

0 comments on commit 477f8c1

Please sign in to comment.