Skip to content

Commit 5d37523

Browse files
authored
Merge pull request #19412 from mozilla/FXA-12224
feat(mfa): Wrap secondary email pages in MfaGuard
2 parents c3d663b + f2fb9d8 commit 5d37523

18 files changed

Lines changed: 355 additions & 324 deletions

File tree

packages/functional-tests/tests/settings/changeEmail.spec.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ test.describe('severity-1 #smoke', () => {
2323

2424
await settings.goto();
2525

26-
await changePrimaryEmail(target, settings, secondaryEmail, newEmail);
26+
await changePrimaryEmail(target, settings, secondaryEmail, newEmail, credentials.email);
2727

2828
await settings.signOut();
2929

@@ -59,7 +59,7 @@ test.describe('severity-1 #smoke', () => {
5959

6060
await settings.goto();
6161

62-
await changePrimaryEmail(target, settings, secondaryEmail, newEmail);
62+
await changePrimaryEmail(target, settings, secondaryEmail, newEmail, credentials.email);
6363

6464
await setNewPassword(
6565
settings,
@@ -101,7 +101,7 @@ test.describe('severity-1 #smoke', () => {
101101

102102
await settings.goto();
103103

104-
await changePrimaryEmail(target, settings, secondaryEmail, secondEmail);
104+
await changePrimaryEmail(target, settings, secondaryEmail, secondEmail, credentials.email);
105105

106106
await setNewPassword(
107107
settings,
@@ -150,7 +150,7 @@ test.describe('severity-1 #smoke', () => {
150150

151151
await settings.goto();
152152

153-
await changePrimaryEmail(target, settings, secondaryEmail, newEmail);
153+
await changePrimaryEmail(target, settings, secondaryEmail, newEmail, credentials.email);
154154
await expect(settings.primaryEmail.status).toHaveText(newEmail);
155155

156156
// Click delete account
@@ -185,6 +185,7 @@ test.describe('severity-1 #smoke', () => {
185185

186186
await settings.goto();
187187
await settings.secondaryEmail.addButton.click();
188+
await settings.confirmMfaGuard(credentials.email);
188189
await secondaryEmail.fillOutEmail(newEmail);
189190
const code: string =
190191
await target.emailClient.getVerifySecondaryCode(newEmail);
@@ -234,9 +235,11 @@ async function changePrimaryEmail(
234235
target: BaseTarget,
235236
settings: SettingsPage,
236237
secondaryEmail: SecondaryEmailPage,
237-
email: string
238+
email: string,
239+
primaryEmail: string,
238240
): Promise<void> {
239241
await settings.secondaryEmail.addButton.click();
242+
await settings.confirmMfaGuard(primaryEmail);
240243
await secondaryEmail.fillOutEmail(email);
241244
const code: string = await target.emailClient.getVerifySecondaryCode(email);
242245
await secondaryEmail.fillOutVerificationCode(code);

packages/functional-tests/tests/settings/changeEmailBlocked.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ test.describe('severity-1 #smoke', () => {
2828
const invalidPassword = testAccountTracker.generatePassword();
2929

3030
await settings.goto();
31-
await changePrimaryEmail(target, settings, secondaryEmail, blockedEmail);
31+
await changePrimaryEmail(target, settings, secondaryEmail, blockedEmail, credentials.email);
3232
await settings.signOut();
3333
await signin.fillOutEmailFirstForm(blockedEmail);
3434
await signin.fillOutPasswordForm(invalidPassword);
@@ -67,7 +67,7 @@ test.describe('severity-1 #smoke', () => {
6767
const blockedEmail = testAccountTracker.generateBlockedEmail();
6868

6969
await settings.goto();
70-
await changePrimaryEmail(target, settings, secondaryEmail, blockedEmail);
70+
await changePrimaryEmail(target, settings, secondaryEmail, blockedEmail, credentials.email);
7171
await settings.signOut();
7272

7373
await signin.fillOutEmailFirstForm(blockedEmail);
@@ -110,9 +110,11 @@ async function changePrimaryEmail(
110110
target: BaseTarget,
111111
settings: SettingsPage,
112112
secondaryEmail: SecondaryEmailPage,
113-
email: string
113+
email: string,
114+
primaryEmail: string,
114115
): Promise<void> {
115116
await settings.secondaryEmail.addButton.click();
117+
await settings.confirmMfaGuard(primaryEmail);
116118
await secondaryEmail.fillOutEmail(email);
117119
const code: string = await target.emailClient.getVerifySecondaryCode(email);
118120
await secondaryEmail.fillOutVerificationCode(code);

packages/functional-tests/tests/signin/signinBlocked.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ test.describe('severity-2 #smoke', () => {
166166

167167
await settings.goto();
168168
await settings.secondaryEmail.addButton.click();
169+
await settings.confirmMfaGuard(credentials.email);
169170
await secondaryEmail.fillOutEmail(blockedEmail);
170171
const verifyCode: string =
171172
await target.emailClient.getVerifySecondaryCode(blockedEmail);

packages/functional-tests/tests/syncV3/fxDesktopV3ForceAuth.spec.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ test.describe('severity-1 #smoke', () => {
128128
uid,
129129
});
130130
await signin.fillOutPasswordForm(credentials.password);
131-
// fails on this chck for react (message not sent)
131+
// fails on this check for react (message not sent)
132132
await fxDesktopV3ForceAuth.checkWebChannelMessage(
133133
FirefoxCommand.LinkAccount
134134
);
@@ -146,7 +146,8 @@ test.describe('severity-1 #smoke', () => {
146146
target,
147147
settings,
148148
secondaryEmail,
149-
nonBlockedEmail
149+
nonBlockedEmail,
150+
credentials.email
150151
);
151152
await settings.deleteAccountButton.click();
152153
await deleteAccount.deleteAccount(credentials.password);
@@ -162,9 +163,11 @@ async function changePrimaryEmail(
162163
target: BaseTarget,
163164
settings: SettingsPage,
164165
secondaryEmail: SecondaryEmailPage,
165-
email: string
166+
email: string,
167+
primaryEmail: string
166168
): Promise<void> {
167169
await settings.secondaryEmail.addButton.click();
170+
await settings.confirmMfaGuard(primaryEmail);
168171
await secondaryEmail.fillOutEmail(email);
169172
const code: string = await target.emailClient.getVerifySecondaryCode(email);
170173
await secondaryEmail.fillOutVerificationCode(code);

packages/fxa-auth-client/lib/client.ts

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,23 +1666,15 @@ export default class AuthClient {
16661666
return this.sessionGet('/recovery_emails', sessionToken, headers);
16671667
}
16681668

1669-
async recoveryEmailCreate(
1670-
sessionToken: hexstring,
1671-
email: string,
1672-
options: {
1673-
verificationMethod?: string;
1674-
} = {},
1675-
headers?: Headers
1676-
) {
1677-
return this.sessionPost(
1678-
'/recovery_email',
1679-
sessionToken,
1680-
{
1681-
email,
1682-
...options,
1683-
},
1684-
headers
1685-
);
1669+
/**
1670+
* Creates a new recovery email for the account.
1671+
* @param jwt Required scope 'mfa:email'
1672+
* @param email Recovery email to add to account
1673+
* @param headers
1674+
* @returns Response
1675+
*/
1676+
async recoveryEmailCreate(jwt: string, email: string, headers?: Headers) {
1677+
return this.jwtPost('/mfa/recovery_email', jwt, { email }, headers);
16861678
}
16871679

16881680
async recoveryEmailDestroy(
@@ -1713,15 +1705,23 @@ export default class AuthClient {
17131705
);
17141706
}
17151707

1708+
/**
1709+
* Verifies a recovery email by confirming the code sent to that email address.
1710+
* @param jwt Required scope 'mfa:email'
1711+
* @param email Recovery email verify
1712+
* @param code Verification code sent to recovery email
1713+
* @param headers
1714+
* @returns Response
1715+
*/
17161716
async recoveryEmailSecondaryVerifyCode(
1717-
sessionToken: hexstring,
1717+
jwt: string,
17181718
email: string,
17191719
code: string,
17201720
headers?: Headers
17211721
): Promise<{}> {
1722-
return this.sessionPost(
1723-
'/recovery_email/secondary/verify_code',
1724-
sessionToken,
1722+
return this.jwtPost(
1723+
'/mfa/recovery_email/secondary/verify_code',
1724+
jwt,
17251725
{ email, code },
17261726
headers
17271727
);

packages/fxa-auth-server/config/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2476,7 +2476,7 @@ const convictConf = convict({
24762476
env: 'MFA__ENABLED',
24772477
},
24782478
actions: {
2479-
default: ['test', '2fa'],
2479+
default: ['test', '2fa', 'email'],
24802480
doc: 'Actions protected by MFA',
24812481
format: Array,
24822482
env: 'MFA__ACTIONS',

packages/fxa-auth-server/docs/swagger/emails-api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ const RECOVERY_EMAIL_POST = {
110110
description: '/recovery_email',
111111
notes: [
112112
dedent`
113-
🔒 Authenticated with session token
113+
🔒 Authenticated with MFA JWT (scope: mfa:email)
114114
Add a secondary email address to the logged-in account. The created address will be unverified and will not replace the primary email address.
115115
`,
116116
],
@@ -212,7 +212,7 @@ const RECOVERY_EMAIL_SECONDARY_VERIFY_CODE_POST = {
212212
description: '/recovery_email/secondary/verify_code',
213213
notes: [
214214
dedent`
215-
🔒 Authenticated with session token
215+
🔒 Authenticated with session MFA JWT (scope: mfa:email)
216216
217217
This endpoint verifies a secondary email using a time based (otp) code.
218218
`,

0 commit comments

Comments
 (0)