From 22692ae8526175b05346f2cd2a02d79da4bb7fdf Mon Sep 17 00:00:00 2001 From: Dan Schomburg <94418270+dschom@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:02:15 -0800 Subject: [PATCH] task(recovery-phone): Add confirmCode method Because: - We want to able to confirm a user's setup code This Commit: - Adds a method called confirmCode to the recovery-phone service - Register the phone number to the account in the event the code provided is valid. --- .../src/lib/recovery-phone.service.spec.ts | 68 ++++++++++++++++++- .../src/lib/recovery-phone.service.ts | 27 ++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/libs/accounts/recovery-phone/src/lib/recovery-phone.service.spec.ts b/libs/accounts/recovery-phone/src/lib/recovery-phone.service.spec.ts index 00c9b0c7d7e..07ec9024d62 100644 --- a/libs/accounts/recovery-phone/src/lib/recovery-phone.service.spec.ts +++ b/libs/accounts/recovery-phone/src/lib/recovery-phone.service.spec.ts @@ -16,7 +16,11 @@ describe('RecoveryPhoneService', () => { const code = '000000'; const mockSmsManager = { sendSMS: jest.fn().mockReturnValue(true) }; - const mockRecoveryPhoneManager = { storeUnconfirmed: jest.fn() }; + const mockRecoveryPhoneManager = { + storeUnconfirmed: jest.fn(), + getUnconfirmed: jest.fn(), + registerPhoneNumber: jest.fn(), + }; const mockOtpManager = { generateCode: jest.fn() }; const mockRecoveryPhoneServiceConfig = { allowedNumbers: ['+1500'], @@ -94,4 +98,66 @@ describe('RecoveryPhoneService', () => { mockError ); }); + + describe('confirm code', () => { + it('can confirm valid sms code', async () => { + mockRecoveryPhoneManager.getUnconfirmed.mockReturnValue({}); + + const result = await service.confirmCode(uid, code); + + expect(result).toBeTruthy(); + expect(mockRecoveryPhoneManager.getUnconfirmed).toBeCalledWith(uid, code); + }); + + it('can confirm valid sms code used for setup', async () => { + mockRecoveryPhoneManager.getUnconfirmed.mockReturnValue({ + isSetup: true, + }); + mockRecoveryPhoneManager.registerPhoneNumber.mockReturnValue(true); + + const result = await service.confirmCode(uid, code); + + expect(result).toBeTruthy(); + expect(mockRecoveryPhoneManager.getUnconfirmed).toBeCalledWith(uid, code); + }); + + it('can confirm valid sms code used for setup', async () => { + mockRecoveryPhoneManager.getUnconfirmed.mockResolvedValue({ + isSetup: true, + phoneNumber, + }); + mockRecoveryPhoneManager.registerPhoneNumber.mockResolvedValue({}); + + const result = await service.confirmCode(uid, code); + + expect(result).toEqual(true); + expect(mockRecoveryPhoneManager.getUnconfirmed).toBeCalledWith(uid, code); + expect(mockRecoveryPhoneManager.registerPhoneNumber).toBeCalledWith( + uid, + phoneNumber + ); + }); + + it('can indicate invalid sms code', async () => { + mockRecoveryPhoneManager.getUnconfirmed.mockReturnValue(null); + + const result = await service.confirmCode(uid, code); + + expect(result).toEqual(false); + expect(mockRecoveryPhoneManager.getUnconfirmed).toBeCalledWith(uid, code); + }); + + it('throws library error while confirming sms code', () => { + mockRecoveryPhoneManager.getUnconfirmed.mockRejectedValueOnce(mockError); + expect(service.confirmCode(uid, code)).rejects.toEqual(mockError); + }); + + it('throws library error while registering phone number for sms code', () => { + mockRecoveryPhoneManager.getUnconfirmed.mockResolvedValue({ + isSetup: true, + }); + mockRecoveryPhoneManager.registerPhoneNumber.mockRejectedValue(mockError); + expect(service.confirmCode(uid, code)).rejects.toEqual(mockError); + }); + }); }); diff --git a/libs/accounts/recovery-phone/src/lib/recovery-phone.service.ts b/libs/accounts/recovery-phone/src/lib/recovery-phone.service.ts index 6cf40816fda..7d07150a5c1 100644 --- a/libs/accounts/recovery-phone/src/lib/recovery-phone.service.ts +++ b/libs/accounts/recovery-phone/src/lib/recovery-phone.service.ts @@ -49,4 +49,31 @@ export class RecoveryPhoneService { ); return true; } + + /** + * Confirms a UID code. This will also and finalizes the phone number setup if the code provided was + * intended for phone number setup. + * @param uid An account id + * @param code A otp code + * @returns True if successful + */ + public async confirmCode(uid: string, code: string) { + const data = await this.recoveryPhoneManager.getUnconfirmed(uid, code); + + // If there is no data, it means there's no record of this code being sent to the uid provided + if (data == null) { + return false; + } + + // If this was for a setup operation. Register the phone number to the uid. + if (data.isSetup === true) { + await this.recoveryPhoneManager.registerPhoneNumber( + uid, + data.phoneNumber + ); + } + + // There was a record matching, the uid / code. The confirmation was successful. + return true; + } }