Skip to content

Commit 5a43625

Browse files
authored
Merge pull request #18363 from mozilla/fxa-remove-unconfirmed
feat(sms): Revoke old recovery phone codes when sending new code
2 parents 1868f1e + 794c4f0 commit 5a43625

File tree

2 files changed

+56
-2
lines changed

2 files changed

+56
-2
lines changed

libs/accounts/recovery-phone/src/lib/recovery-phone.service.spec.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ describe('RecoveryPhoneService', () => {
114114
expect(result).toBeTruthy();
115115
});
116116

117-
it('Should send new code for setup phone number ', async () => {
117+
it('Should send new code for setup phone number', async () => {
118118
mockOtpManager.generateCode.mockReturnValue(code);
119119
mockRecoveryPhoneManager.getAllUnconfirmed.mockResolvedValue([
120120
'this:is:the:code123',
@@ -448,6 +448,48 @@ describe('RecoveryPhoneService', () => {
448448
mockRecoveryPhoneManager.getConfirmedPhoneNumber.mockRejectedValue(error);
449449
expect(service.sendCode(uid)).rejects.toThrow(error);
450450
});
451+
452+
it('Should send new code for setup phone number', async () => {
453+
mockOtpManager.generateCode.mockReturnValue(code);
454+
mockRecoveryPhoneManager.getAllUnconfirmed.mockResolvedValue([
455+
'this:is:the:code123',
456+
'this:is:the:code456',
457+
]);
458+
459+
mockRecoveryPhoneManager.getConfirmedPhoneNumber.mockResolvedValueOnce({
460+
phoneNumber,
461+
});
462+
mockOtpManager.generateCode.mockResolvedValueOnce(code);
463+
mockSmsManager.sendSMS.mockResolvedValue({ status: 'success' });
464+
465+
const result = await service.sendCode(uid);
466+
467+
expect(result).toBeTruthy();
468+
expect(mockRecoveryPhoneManager.getConfirmedPhoneNumber).toBeCalledWith(
469+
uid
470+
);
471+
expect(mockRecoveryPhoneManager.storeUnconfirmed).toBeCalledWith(
472+
uid,
473+
code,
474+
phoneNumber,
475+
false
476+
);
477+
expect(mockOtpManager.generateCode).toBeCalled();
478+
expect(mockSmsManager.sendSMS).toBeCalledWith({
479+
to: phoneNumber,
480+
body: code,
481+
});
482+
483+
expect(mockRecoveryPhoneManager.removeCode).toBeCalledWith(
484+
uid,
485+
'code123'
486+
);
487+
expect(mockRecoveryPhoneManager.removeCode).toBeCalledWith(
488+
uid,
489+
'code456'
490+
);
491+
expect(mockRecoveryPhoneManager.getAllUnconfirmed).toBeCalledWith(uid);
492+
});
451493
});
452494

453495
describe('available', () => {

libs/accounts/recovery-phone/src/lib/recovery-phone.service.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,8 @@ export class RecoveryPhoneService {
331331
}
332332

333333
/**
334-
* Sends an totp code to a user
334+
* Sends a new totp code to a user and revokes any previous unconfirmed codes.
335+
*
335336
* @param uid Account id
336337
* @param getFormattedMessage Optional template function to format the message
337338
* @returns True if message didn't fail to send.
@@ -344,6 +345,17 @@ export class RecoveryPhoneService {
344345
throw new RecoveryPhoneNotEnabled();
345346
}
346347

348+
// Invalidate and remove any or all previous unconfirmed code entries
349+
const unconfirmedKeys = await this.recoveryPhoneManager.getAllUnconfirmed(
350+
uid
351+
);
352+
for (const key of unconfirmedKeys) {
353+
const oldCode = key.split(':').pop();
354+
if (oldCode) {
355+
await this.recoveryPhoneManager.removeCode(uid, oldCode);
356+
}
357+
}
358+
347359
const { phoneNumber } =
348360
await this.recoveryPhoneManager.getConfirmedPhoneNumber(uid);
349361
const code = await this.otpCode.generateCode();

0 commit comments

Comments
 (0)