Skip to content

Commit

Permalink
Finish up email updates
Browse files Browse the repository at this point in the history
  • Loading branch information
bcomnes committed Dec 11, 2022
1 parent 7d67caf commit 6941975
Show file tree
Hide file tree
Showing 15 changed files with 527 additions and 175 deletions.
24 changes: 10 additions & 14 deletions routes/api/register/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable camelcase */
import SQL from '@nearform/sql'
import S from 'fluent-json-schema'
import { verifyEmailBody } from '../user/email/resend-account-confirmation.js'

const newUserJsonSchema = S.object()
.prop('username',
Expand Down Expand Up @@ -88,12 +89,19 @@ export default async function registerRoutes (fastify, opts) {
`)

if (blackholeResults.rows.length === 0 || blackholeResults.rows[0].disabled === false) {
await fastify.email.sendMail({
await Promise.allSettled([fastify.email.sendMail({
from: `"Breadcrum.net 🥖" <${fastify.config.APP_EMAIL}>`,
to: email,
subject: 'Verify your account email address', // Subject line
text: verifyEmailBody({ email: user.email, username: user.username, host: fastify.config.HOST, token: email_verify_token })
text: verifyEmailBody({
email: user.email,
username: user.username,
host: fastify.config.HOST,
transport: fastify.config.TRANSPORT,
token: email_verify_token
})
})
])
} else {
fastify.log.warn({ email }, 'Skipping email for blocked email address')
}
Expand All @@ -107,15 +115,3 @@ export default async function registerRoutes (fastify, opts) {
}
)
}

function verifyEmailBody ({ email, username, host, token }) {
return `Hi ${username},
Thanks for signing up for a Breadcrum.net account. Please verify your email address by clicking the link below.
https://${host}/account/verify-email?token=${token}&update=${true}
If you did not sign up for this account, please contact support@breadcrum.net or perform a password reset on the account associated with this email address and perform an account delete action if this is unwanted.
Thank you!`
}
102 changes: 0 additions & 102 deletions routes/api/user/email/confirm-email.js

This file was deleted.

34 changes: 34 additions & 0 deletions routes/api/user/email/delete-email.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* eslint-disable camelcase */
import SQL from '@nearform/sql'

// Delete any pendig email updates
export async function deleteEmail (fastify, opts) {
fastify.delete(
'/',
{
preHandler: fastify.auth([fastify.verifyJWT])
},
async function deleteEmailHandler (request, reply) {
return fastify.pg.transact(async client => {
const userID = request.user.id

const updates = [
SQL`pending_email_update = null`,
SQL`pending_email_update_token = null`,
SQL`pending_email_update_token_exp = null`
]

const updateQuery = SQL`
update users
set ${SQL.glue(updates, ' , ')}
where id = ${userID}
returning username, email, pending_email_update, pending_email_update_token, pending_email_update_token_exp;
`

await client.query(updateQuery)

reply.code(204)
})
}
)
}
8 changes: 5 additions & 3 deletions routes/api/user/email/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { confirmEmail } from './confirm-email.js'
import { resendEmailVerification } from './resend-confirmation.js'
import { postEmail } from './post-email.js'
import { verifyEmail } from './verify-email.js'
import { deleteEmail } from './delete-email.js'

export default async function bookmarksRoutes (fastify, opts) {
await Promise.all([
confirmEmail(fastify, opts),
resendEmailVerification(fastify, opts),
verifyEmail(fastify, opts),
postEmail(fastify, opts),
verifyEmail(fastify, opts)
deleteEmail(fastify, opts)
])
}
15 changes: 10 additions & 5 deletions routes/api/user/email/post-email.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import SQL from '@nearform/sql'
import { EMAIL_CONFIRM_TOKEN_EXP, EMAIL_CONFIRM_TOKEN } from './email-confirm-tokens.js'

// Update the email address by setting a pending_email_update field.
export async function postEmail (fastify, opts) {
fastify.post(
'/',
Expand Down Expand Up @@ -87,6 +88,7 @@ export async function postEmail (fastify, opts) {
text: verifyEmailUpdateBody({
username: updatedUser.username,
host: fastify.config.HOST,
transport: fastify.config.TRANSPORT,
token: updatedUser.pending_email_update_token,
oldEmail: updatedUser.email,
newEmail: updatedUser.pending_email_update
Expand All @@ -109,10 +111,11 @@ export async function postEmail (fastify, opts) {
fastify.email.sendMail({
from: `"Breadcrum.net 🥖" <${fastify.config.APP_EMAIL}>`,
to: updatedUser.email,
subject: 'Verify your updated email address', // Subject line
subject: verifyEmailSubject,
text: notifyOldEmailBody({
username: updatedUser.username,
host: fastify.config.HOST,
transport: fastify.config.TRANSPORT,
token: updatedUser.pending_email_update_token,
oldEmail: updatedUser.email,
newEmail: updatedUser.pending_email_update
Expand All @@ -123,7 +126,7 @@ export async function postEmail (fastify, opts) {
fastify.log.warn({ email: updatedUser.email }, 'Skipping email for blocked email address')
}

return await Promise.all(emailJobs)
return await Promise.allSettled(emailJobs)
})

reply.code(202)
Expand All @@ -139,12 +142,14 @@ export async function postEmail (fastify, opts) {
)
}

function verifyEmailUpdateBody ({ oldEmail, newEmail, username, host, token }) {
export const verifyEmailSubject = 'Email update request notificaiton'

export function verifyEmailUpdateBody ({ transport, oldEmail, newEmail, username, host, token }) {
return `Hi ${username},
If you requested to change your Breadcrum.net account email address from ${oldEmail} to ${newEmail}, click the following link to confir the change.
If you requested to change your Breadcrum.net account email address from ${oldEmail} to ${newEmail}, click the following link to confirm the change.
https://${host}/account/verify-email?token=${token}&update=${true}
${transport}://${host}/email_confirm?token=${token}&update=${true}
If you did not request this change, please immediately change your password and contact support@breadcrum.net
Expand Down
82 changes: 82 additions & 0 deletions routes/api/user/email/resend-account-confirmation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* eslint-disable camelcase */
import SQL from '@nearform/sql'
import { EMAIL_CONFIRM_TOKEN, EMAIL_CONFIRM_TOKEN_EXP } from './email-confirm-tokens.js'

export async function resendAccountEmailVerificationHandler ({
userID, client, reply, fastify
}) {
const verifyQuery = SQL`
select id, email, username, email_confirmed, email_verify_token, email_verify_token_exp
from users
where id = ${userID}
fetch first row only;
`

const results = await client.query(verifyQuery)
const user = results.rows.pop()

if (user.email_confirmed) {
return reply.unprocessableEntity('Email is already confirmed')
}

const updates = [
SQL`email_verify_token = ${EMAIL_CONFIRM_TOKEN}`,
SQL`email_verify_token_exp = ${EMAIL_CONFIRM_TOKEN_EXP}`
]

const updateQuery = SQL`
update users
set ${SQL.glue(updates, ' , ')}
where id = ${userID}
returning username, email, email_verify_token, email_verify_token_exp;
`

const queryResults = await client.query(updateQuery)
const updatedUser = queryResults.rows.pop()

fastify.pqueue.add(async () => {
const blackholeResults = await fastify.pg.query(SQL`
select email, bounce_count, disabled
from email_blackhole
where email = ${updatedUser.email}
fetch first row only;
`)

if (blackholeResults.rows.length === 0 || blackholeResults.rows[0].disabled === false) {
return await Promise.allSettled([
fastify.email.sendMail({
from: `"Breadcrum.net 🥖" <${fastify.config.APP_EMAIL}>`,
to: updatedUser.email,
subject: 'Verify your email address', // Subject line
text: verifyEmailBody({
username: updatedUser.username,
transport: fastify.config.TRANSPORT,
host: fastify.config.HOST,
token: updatedUser.email_verify_token,
oldEmail: updatedUser.email,
newEmail: updatedUser.pending_email_update
})
})
])
} else {
fastify.log.warn({ email: updatedUser.email }, 'Skipping email for blocked email address')
}
})

reply.code(202)
return {
status: 'ok'
}
}

export function verifyEmailBody ({ email, username, transport, host, token }) {
return `Hi ${username},
Thanks for signing up for a Breadcrum.net account. Please verify your email address by clicking the link below.
${transport}://${host}/email_confirm?token=${token}
If you did not sign up for this account, please contact support@breadcrum.net or perform a password reset on the account associated with this email address and perform an account delete action if this is unwanted.
Thank you!`
}
45 changes: 45 additions & 0 deletions routes/api/user/email/resend-confirmation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* eslint-disable camelcase */
import { resendAccountEmailVerificationHandler } from './resend-account-confirmation.js'
import { resendPendingEmailVerificationHandler } from './resend-pending-confirmation.js'

// Request a email verification email
export async function resendEmailVerification (fastify, opts) {
fastify.post(
'::resend',
{
preHandler: fastify.auth([fastify.verifyJWT]),
schema: {
body: {
type: 'object',
properties: {
update: {
type: 'boolean'
}
}
}
},
respose: {
202: {
type: 'object',
properties: {
status: {
type: 'string'
}
}
}
}
},
async function resendEmailVerificationHandler (request, reply) {
return fastify.pg.transact(async client => {
const userID = request.user.id
const { update } = request.body

if (update) {
return await resendPendingEmailVerificationHandler({ userID, client, reply, fastify })
} else {
return await resendAccountEmailVerificationHandler({ userID, client, reply, fastify })
}
})
}
)
}

0 comments on commit 6941975

Please sign in to comment.