Skip to content

Commit f961c32

Browse files
committed
add password reset otp
1 parent 0ab4b76 commit f961c32

File tree

3 files changed

+93
-24
lines changed

3 files changed

+93
-24
lines changed

libs/accounts/email-renderer/src/templates/passwordForgotOtp/en.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Variables:
22
# $code (String) - The confirmation code for sign-in
3-
password-forgot-otp-subject-2 = Use { $code } to change your password
3+
password-forgot-otp-subject-2 = Use { $code } to change your password, yo
44
password-forgot-otp-preview = This code expires in 10 minutes
55
password-forgot-otp-title = Forgot your password?
66
password-forgot-otp-request = We received a request for a password change on your { -product-mozilla-account } from:

packages/fxa-auth-server/lib/routes/password.ts

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,37 +1023,54 @@ module.exports = function (
10231023
request.setMetricsFlowCompleteSignal(flowCompleteSignal);
10241024

10251025
const code = await otpManager.create(account.uid);
1026-
const ip = request.app.clientAddress;
1027-
const service = payload.service || request.query.service;
1028-
const { deviceId, flowId, flowBeginTime } =
1029-
await request.app.metricsContext;
1026+
// const ip = request.app.clientAddress;
1027+
// const service = payload.service || request.query.service;
1028+
// const { deviceId, flowId, flowBeginTime } =
1029+
// await request.app.metricsContext;
10301030
const geoData = request.app.geo;
10311031
const {
10321032
browser: uaBrowser,
1033-
browserVersion: uaBrowserVersion,
10341033
os: uaOS,
10351034
osVersion: uaOSVersion,
1036-
deviceType: uaDeviceType,
10371035
} = request.app.ua;
10381036

1039-
await mailer.sendPasswordForgotOtpEmail(account.emails, account, {
1037+
await fxaMailer.sendPasswordForgotOtpEmail({
1038+
to: fxaMailer.splitEmails(account.emails).to,
10401039
code,
1041-
service,
1042-
acceptLanguage: request.app.acceptLanguage,
1043-
deviceId,
1044-
flowId,
1045-
flowBeginTime,
1046-
ip,
1047-
location: geoData.location,
1048-
timeZone: geoData.timeZone,
1049-
uaBrowser,
1050-
uaBrowserVersion,
1051-
uaOS,
1052-
uaOSVersion,
1053-
uaDeviceType,
1054-
uid: account.uid,
1040+
layoutTemplateValues: {},
1041+
templateValue: {
1042+
acceptLanguage: request.app.acceptLanguage,
1043+
device: {
1044+
uaBrowser,
1045+
uaOSVersion,
1046+
uaOS,
1047+
},
1048+
location: {
1049+
city: geoData.location?.city || '',
1050+
country: geoData.location?.country || '',
1051+
stateCode: geoData.location?.state || '',
1052+
},
1053+
},
10551054
});
10561055

1056+
// await mailer.sendPasswordForgotOtpEmail(account.emails, account, {
1057+
// code,
1058+
// service,
1059+
// acceptLanguage: request.app.acceptLanguage,
1060+
// deviceId,
1061+
// flowId,
1062+
// flowBeginTime,
1063+
// ip,
1064+
// location: geoData.location,
1065+
// timeZone: geoData.timeZone,
1066+
// uaBrowser,
1067+
// uaBrowserVersion,
1068+
// uaOS,
1069+
// uaOSVersion,
1070+
// uaDeviceType,
1071+
// uid: account.uid,
1072+
// });
1073+
10571074
glean.resetPassword.otpEmailSent(request);
10581075

10591076
await request.emitMetricsEvent('password.forgot.send_otp.completed');
@@ -1251,6 +1268,7 @@ module.exports = function (
12511268
resume: payload.resume,
12521269
timeZone: geoData.timeZone,
12531270
acceptLanguage: request.app.acceptLanguage,
1271+
layoutTemplateValues: {},
12541272
headers: {
12551273
'X-Device-Id': deviceId,
12561274
'X-Flow-Id': flowId,

packages/fxa-auth-server/lib/senders/fxa-mailer.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,20 @@ type RecoveryEmailInput = {
3232
>;
3333
};
3434

35-
// TODO: Don't love the name here, but wanted to avoid confusion with the existing mailer. Come up with a new one later.
35+
type PasswordForgotOtpEmailInput = {
36+
to: string;
37+
code: string;
38+
headers?: Record<string, string>;
39+
layoutTemplateValues?: Partial<Omit<renderer.TemplateData, 'privacyUrl'>>;
40+
templateValue: Omit<
41+
renderer.passwordForgotOtp.TemplateData,
42+
'code' | 'supportUrl' | 'supportLinkAttributes' | 'time' | 'date'
43+
> & {
44+
timeZone?: string;
45+
acceptLanguage?: string;
46+
};
47+
};
48+
3649
export class FxaMailer extends FxaEmailRenderer {
3750
public helpers: renderer.EmailHelpers;
3851

@@ -45,6 +58,7 @@ export class FxaMailer extends FxaEmailRenderer {
4558
super(bindings);
4659
this.helpers = new renderer.EmailHelpers();
4760
}
61+
4862
async sendRecoveryEmail(opts: RecoveryEmailInput) {
4963
const templateName = renderer.recovery.template;
5064

@@ -87,7 +101,7 @@ export class FxaMailer extends FxaEmailRenderer {
87101
{
88102
sync: false,
89103
...opts.layoutTemplateValues,
90-
privacyUrl: privacyUrl.toString(),
104+
privacyUrl,
91105
}
92106
);
93107

@@ -105,6 +119,43 @@ export class FxaMailer extends FxaEmailRenderer {
105119
});
106120
}
107121

122+
async sendPasswordForgotOtpEmail(opts: PasswordForgotOtpEmailInput) {
123+
// these could probably be grouped together with time and date to a little
124+
// helper method that returns all 4 values. they'll likely be used everywhere
125+
const { privacyUrl, supportUrl } =
126+
this.linkBuilder.buildCommonLinks('passwordForgotOtp');
127+
const { timeNow: time, dateNow: date } =
128+
this.helpers.constructLocalTimeString(
129+
opts.templateValue.timeZone,
130+
opts.templateValue.acceptLanguage
131+
);
132+
const rendered = await this.renderPasswordForgotOtp(
133+
{
134+
...opts.templateValue,
135+
time,
136+
date,
137+
code: opts.code,
138+
supportUrl,
139+
},
140+
{
141+
sync: false,
142+
...opts.layoutTemplateValues,
143+
privacyUrl,
144+
}
145+
);
146+
const headers = {
147+
...opts.headers,
148+
'X-Password-Forgot-Otp': opts.code,
149+
};
150+
151+
return this.emailSender.send({
152+
to: opts.to,
153+
from: this.mailerConfig.sender,
154+
headers,
155+
...rendered,
156+
});
157+
}
158+
108159
splitEmails(emails: Email[]) {
109160
return emails.reduce(
110161
(result: { to: string; cc: string[] }, item) => {

0 commit comments

Comments
 (0)