diff --git a/packages/fxa-auth-server/lib/routes/password.ts b/packages/fxa-auth-server/lib/routes/password.ts index 419c9f7b57b..102203b662b 100644 --- a/packages/fxa-auth-server/lib/routes/password.ts +++ b/packages/fxa-auth-server/lib/routes/password.ts @@ -999,8 +999,15 @@ module.exports = function ( request.validateMetricsContext(); const account = await db.accountRecord(email); - if (!emailsMatch(account.primaryEmail.normalizedEmail, email)) { - throw error.cannotResetPasswordWithSecondaryEmail(); + + const isPrimaryOrVerifiedEmail = + emailsMatch(account.primaryEmail.normalizedEmail, email) || + account.emails.some( + (e) => e.isVerified && emailsMatch(e.normalizedEmail, email) + ); + + if (!isPrimaryOrVerifiedEmail) { + throw error.unknownAccount(); } let flowCompleteSignal; @@ -1193,8 +1200,14 @@ module.exports = function ( ]); const accountRecord = await db.accountRecord(email); - if (!emailsMatch(accountRecord.primaryEmail.normalizedEmail, email)) { - throw error.cannotResetPasswordWithSecondaryEmail(); + const isPrimaryOrVerifiedEmail = + emailsMatch(accountRecord.primaryEmail.normalizedEmail, email) || + accountRecord.emails.some( + (e) => e.isVerified && emailsMatch(e.normalizedEmail, email) + ); + + if (!isPrimaryOrVerifiedEmail) { + throw error.unknownAccount(); } // The token constructor sets createdAt from its argument. // Clobber the timestamp to prevent prematurely expired tokens. diff --git a/packages/fxa-auth-server/test/remote/recovery_email_emails.js b/packages/fxa-auth-server/test/remote/recovery_email_emails.js index 841ad884779..93a29758c21 100644 --- a/packages/fxa-auth-server/test/remote/recovery_email_emails.js +++ b/packages/fxa-auth-server/test/remote/recovery_email_emails.js @@ -699,7 +699,7 @@ const password = 'allyourbasearebelongtous'; }); }); - describe("shouldn't be able to initiate account reset from secondary email", () => { + describe('should be able to initiate account reset from verified secondary email', () => { let secondEmail; beforeEach(() => { secondEmail = server.uniqueEmail(); @@ -716,7 +716,28 @@ const password = 'allyourbasearebelongtous'; }); }); - it('fails to initiate account reset with known secondary email', () => { + it('can initiate account reset with verified secondary email', () => { + client.email = secondEmail; + return client.forgotPassword().then(() => { + assert.ok( + client.passwordForgotToken, + 'was able to initiate reset password' + ); + }); + }); + }); + + describe("shouldn't be able to initiate account reset from secondary email", () => { + let secondEmail; + beforeEach(() => { + secondEmail = server.uniqueEmail(); + return client.createEmail(secondEmail).then((res) => { + assert.ok(res, 'ok response'); + return server.mailbox.waitForEmail(secondEmail); + }); + }); + + it('fails to initiate account reset with unverified secondary email', () => { client.email = secondEmail; return client .forgotPassword() @@ -727,7 +748,7 @@ const password = 'allyourbasearebelongtous'; }) .catch((err) => { assert.equal(err.code, 400, 'correct error code'); - assert.equal(err.errno, 145, 'correct errno code'); + assert.equal(err.errno, 102, 'correct errno code'); }); });