Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions libs/accounts/email-renderer/src/renderer/email-link-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,40 @@ export class EmailLinkBuilder {
buildDesktopLink() {
return this.config.firefoxDesktopUrl;
}

buildVerifyEmailLink(
templateName: string,
metricsEnabled: boolean,
query: {
code: string;
uid: string;
resume?: string;
redirectTo?: string;
service?: string;
}
): string {
const url = new URL(`${this.baseUri}/verify_email`);
if (this.config.prependVerificationSubdomain.enabled) {
url.host = `${this.config.prependVerificationSubdomain.subdomain}.${url.host}`;
}
this.addUTMParams(url, templateName, metricsEnabled);
this.addQueryParams(url, query);
return url.toString();
}

buildReportSignInLink(
templateName: string,
metricsEnabled: boolean,
query: {
uid: string;
unblockCode: string;
}
): string {
const url = new URL(`${this.baseUri}/report_signin`);
this.addUTMParams(url, templateName, metricsEnabled, 'report');
this.addQueryParams(url, query);
return url.toString();
}
}

// PORTED FROM fxa-shared/subscriptions/configuration/utils.ts
Expand Down
61 changes: 39 additions & 22 deletions packages/fxa-auth-server/lib/routes/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,28 +403,45 @@ export class AccountHandler {
break;
}
default: {
await this.mailer.sendVerifyEmail([], account, {
code: account.emailCode,
service: form.service || query.service,
redirectTo: form.redirectTo,
resume: form.resume,
acceptLanguage: locale,
deviceId,
flowId,
flowBeginTime,
productId,
planId,
ip,
location: request.app.geo.location,
timeZone: request.app.geo.timeZone,
style,
uaBrowser: sessionToken.uaBrowser,
uaBrowserVersion: sessionToken.uaBrowserVersion,
uaOS: sessionToken.uaOS,
uaOSVersion: sessionToken.uaOSVersion,
uaDeviceType: sessionToken.uaDeviceType,
uid: sessionToken.uid,
});
if (this.fxaMailer.canSend('verify')) {
await this.fxaMailer.sendVerifyEmail({
...FxaMailerFormat.account(account),
...(await FxaMailerFormat.metricsContext(request)),
...FxaMailerFormat.sync(form.service || query.service),
...FxaMailerFormat.localTime(request),
...FxaMailerFormat.location(request),
...FxaMailerFormat.device(request),
code: account.emailCode,
resume: form.resume,
redirectTo: form.redirectTo,
service: form.service || query.service,
});
} else {
console.debug('falling back')
const sent = await this.mailer.sendVerifyEmail([], account, {
code: account.emailCode,
service: form.service || query.service,
redirectTo: form.redirectTo,
resume: form.resume,
acceptLanguage: locale,
deviceId,
flowId,
flowBeginTime,
productId,
planId,
ip,
location: request.app.geo.location,
timeZone: request.app.geo.timeZone,
style,
uaBrowser: sessionToken.uaBrowser,
uaBrowserVersion: sessionToken.uaBrowserVersion,
uaOS: sessionToken.uaOS,
uaOSVersion: sessionToken.uaOSVersion,
uaDeviceType: sessionToken.uaDeviceType,
uid: sessionToken.uid,
});
console.debug('falling back sent!', sent);
}
}
}

Expand Down
204 changes: 138 additions & 66 deletions packages/fxa-auth-server/lib/routes/emails.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,32 +236,45 @@ module.exports = (

const geoData = request.app.geo;
try {
await mailer.sendVerifySecondaryCodeEmail(
[
if (fxaMailer.canSend('verifySecondaryCode')) {
await fxaMailer.sendVerifySecondaryCodeEmail({
...FxaMailerFormat.account(account),
...(await FxaMailerFormat.metricsContext(request)),
...FxaMailerFormat.localTime(request),
...FxaMailerFormat.location(request),
...FxaMailerFormat.device(request),
...FxaMailerFormat.sync(false),
code: otpUtils.generateOtpCode(secret, otpOptions),
email,
});
} else {
await mailer.sendVerifySecondaryCodeEmail(
[
{
email,
normalizedEmail,
isVerified: false,
isPrimary: false,
uid,
},
],
sessionToken,
{
code: otpUtils.generateOtpCode(secret, otpOptions),
deviceId: sessionToken.deviceId,
acceptLanguage: request.app.acceptLanguage,
email,
normalizedEmail,
isVerified: false,
isPrimary: false,
primaryEmail,
location: geoData.location,
timeZone: geoData.timeZone,
uaBrowser: sessionToken.uaBrowser,
uaBrowserVersion: sessionToken.uaBrowserVersion,
uaOS: sessionToken.uaOS,
uaOSVersion: sessionToken.uaOSVersion,
uid,
},
],
sessionToken,
{
code: otpUtils.generateOtpCode(secret, otpOptions),
deviceId: sessionToken.deviceId,
acceptLanguage: request.app.acceptLanguage,
email,
primaryEmail,
location: geoData.location,
timeZone: geoData.timeZone,
uaBrowser: sessionToken.uaBrowser,
uaBrowserVersion: sessionToken.uaBrowserVersion,
uaOS: sessionToken.uaOS,
uaOSVersion: sessionToken.uaOSVersion,
uid,
}
);
}
);
}
} catch (err) {
log.error('secondary_email.sendVerifySecondaryCodeEmail.error', {
err: err,
Expand Down Expand Up @@ -764,8 +777,6 @@ module.exports = (
// This endpoint can resend multiple types of codes, set these values once it
// is known what is being verified.
let code;
let verifyFunction;
let event;
let emails = [];

// Return immediately if this session or token is already verified. Only exception
Expand All @@ -790,9 +801,8 @@ module.exports = (
return {};
}

setVerifyFunction();

const { flowId, flowBeginTime } = await request.app.metricsContext;
const account = await db.account(sessionToken.uid);

const mailerOpts = {
code,
Expand All @@ -816,8 +826,69 @@ module.exports = (
style,
};

await verifyFunction(emails, sessionToken, mailerOpts);
await request.emitMetricsEvent(`email.${event}.resent`);
if (type && type === 'upgradeSession') {
if (fxaMailer.canSend('verifyPrimary')) {
await fxaMailer.sendVerifyPrimaryEmail({
...FxaMailerFormat.account(account),
...(await FxaMailerFormat.metricsContext(request)),
...FxaMailerFormat.localTime(request),
...FxaMailerFormat.location(request),
...FxaMailerFormat.device(request),
...FxaMailerFormat.sync(service),
code,
service,
redirectTo: request.payload.redirectTo,
resume: request.payload.resume,
});
} else {
await mailer.sendVerifyPrimaryEmail(
emails,
sessionToken,
mailerOpts
);
}
await request.emitMetricsEvent(
`email.verification_email_primary.resent`
);
} else if (!sessionToken.emailVerified) {
if (fxaMailer.canSend('verify')) {
await fxaMailer.sendVerifyEmail({
...FxaMailerFormat.account(account),
...(await FxaMailerFormat.metricsContext(request)),
...FxaMailerFormat.sync(service),
...FxaMailerFormat.localTime(request),
...FxaMailerFormat.location(request),
...FxaMailerFormat.device(request),
code,
service,
redirectTo: request.payload.redirectTo,
resume: request.payload.resume,
});
} else {
await mailer.sendVerifyEmail(emails, sessionToken, mailerOpts);
}
await request.emitMetricsEvent(`email.verification.resent`);
} else {
if (fxaMailer.canSend('verifyLogin')) {
await fxaMailer.sendVerifyLoginEmail({
...FxaMailerFormat.account(account),
...(await FxaMailerFormat.metricsContext(request)),
...FxaMailerFormat.localTime(request),
...FxaMailerFormat.location(request),
...FxaMailerFormat.device(request),
...FxaMailerFormat.sync(service),
code,
service,
clientName: 'Firefox',
redirectTo: request.payload.redirectTo,
resume: request.payload.resume,
});
} else {
await mailer.sendVerifyLoginEmail(emails, sessionToken, mailerOpts);
}
await request.emitMetricsEvent(`email.confirmation.resent`);
}

return {};

// Returns a boolean to indicate whether to send email.
Expand Down Expand Up @@ -863,19 +934,6 @@ module.exports = (
return true;
}
}

function setVerifyFunction() {
if (type && type === 'upgradeSession') {
verifyFunction = mailer.sendVerifyPrimaryEmail;
event = 'verification_email_primary';
} else if (!sessionToken.emailVerified) {
verifyFunction = mailer.sendVerifyEmail;
event = 'verification';
} else {
verifyFunction = mailer.sendVerifyLoginEmail;
event = 'confirmation';
}
}
},
},
{
Expand Down Expand Up @@ -1334,6 +1392,7 @@ module.exports = (
const sessionToken = request.auth.credentials;
const { email } = request.payload;
const normalizedEmail = normalizeEmail(email);
const account = await db.account(sessionToken.uid);

await customs.checkAuthenticated(
request,
Expand Down Expand Up @@ -1424,33 +1483,46 @@ module.exports = (

const geoData = request.app.geo;
try {
await mailer.sendVerifySecondaryCodeEmail(
[
if (fxaMailer.canSend('verifySecondaryCode')) {
await fxaMailer.sendVerifySecondaryCodeEmail({
...FxaMailerFormat.account(account),
...(await FxaMailerFormat.metricsContext(request)),
...FxaMailerFormat.localTime(request),
...FxaMailerFormat.location(request),
...FxaMailerFormat.device(request),
...FxaMailerFormat.sync(false),
code,
email,
});
} else {
await mailer.sendVerifySecondaryCodeEmail(
[
{
email,
normalizedEmail,
isVerified: false,
isPrimary: false,
uid,
},
],
sessionToken,
{
code,
deviceId,
acceptLanguage: request.app.acceptLanguage,
email,
normalizedEmail,
isVerified: false,
isPrimary: false,
primaryEmail: sessionToken.email,
location: geoData.location,
timeZone: geoData.timeZone,
uaBrowser,
uaBrowserVersion,
uaOS,
uaOSVersion,
uaDeviceType,
uid,
},
],
sessionToken,
{
code,
deviceId,
acceptLanguage: request.app.acceptLanguage,
email,
primaryEmail: sessionToken.email,
location: geoData.location,
timeZone: geoData.timeZone,
uaBrowser,
uaBrowserVersion,
uaOS,
uaOSVersion,
uaDeviceType,
uid,
}
);
}
);
}
} catch (err) {
log.error('secondary_email.resendVerifySecondaryCodeEmail.error', {
err: err,
Expand Down
Loading
Loading