feat(fxa-settings): Implement using recovery phone to sign in#18255
feat(fxa-settings): Implement using recovery phone to sign in#18255vpomerleau merged 1 commit intomainfrom
Conversation
abe0538 to
406b221
Compare
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
|
||
| import { ConfigService } from '@nestjs/config'; | ||
| import { SmsManagerConfig } from './sms.manger.config'; |
| }); | ||
|
|
||
| const mockAuthClient = new AuthClient('http://localhost:9000', { | ||
| keyStretchVersion: 1, |
There was a problem hiding this comment.
Guilty of copy pasta from another test file - @dschom should this be updated to v2 in all auth client mocks?
1e644ab to
a08013f
Compare
vbudhram
left a comment
There was a problem hiding this comment.
@vpomerleau Took a first pass at this. Gonna do some manual testing now
| user.type(screen.getByRole('textbox'), MOCK_RECOVERY_CODE) | ||
| ); | ||
|
|
||
| expect(screen.getByRole('button', { name: /Confirm/i })).toHaveAttribute( |
| return; | ||
| } | ||
| try { | ||
| await authClient.recoveryPhoneSigninSendCode(signinState.sessionToken); |
There was a problem hiding this comment.
FWIW, it seems odd to me to send the code from this page. I wonder if the recovery phone page should handle sending codes instead. Don't need to block on this though.
There was a problem hiding this comment.
I don't disagree, but we'd have to review UX for error handling in that scenario because the designs currently have us showing an error on the choice screen if send fails for the regular path to recovery phone.
I'll file a follow up ticket to review if we should send the code before or after navigation.
| signin-recovery-code-instruction-v2 = Enter one of the one-time use backup authentication codes you saved during two-step authentication setup. | ||
| signin-recovery-code-input-label-v2 = Enter 10-character code | ||
| # codes here refers to backup authentication codes | ||
| signin-recovery-code-instruction-v3 = Enter one of the one-time-use codes you saved when you set up two-step authentication. |
There was a problem hiding this comment.
| signin-recovery-code-instruction-v3 = Enter one of the one-time-use codes you saved when you set up two-step authentication. | |
| signin-recovery-code-instruction-v3 = Enter one of the one-time use codes you saved when you set up two-step authentication. |
Nit, but aligns with previous string in terms of hyphenation.
There was a problem hiding this comment.
Content reviewed and recommended "one-time-use" codes, so this is an intentional update :)
vbudhram
left a comment
There was a problem hiding this comment.
@vpomerleau Manual testing looks good! I think this will be ok to merge, I can still do some fixes while working on #18287.
🚀
| 'force_auth', | ||
| 'signin_recovery_code', | ||
| 'signin_recovery_choice', | ||
| 'signin_recovery_phone', |
There was a problem hiding this comment.
You probably already know this but just to be clear / for others too: we only need to add routes to content-server this way if it's not behind /settings/* - content-server already points all settings routes to our React app if users hit them directly. Otherwise, without these content-server modifications, the server doesn't know what to do on a refresh of that page and it ends up blank, so this is probably preferred for routes not behind settings.
(I wanted to think that through myself too, so, 👍)
There was a problem hiding this comment.
Thank you, I wasn't 💯 sure on this so appreciate the confirmation!
| }), | ||
| mockRecoveryPhoneGet = jest.fn().mockResolvedValue({ | ||
| exists: true, | ||
| phoneNumber: '7890', |
There was a problem hiding this comment.
Right now this will return MOCK_MASKED_PHONE_NUMBER FWIW, and not just the last 4, if the session isn't verified. I need to fill out details for FXA-11044.
There was a problem hiding this comment.
Thank you, I'll update to test that the masked number is handled properly!
| const [loading, setLoading] = useState(true); | ||
|
|
||
| useEffect(() => { | ||
| if (!signinState || !signinState.sessionToken) { |
There was a problem hiding this comment.
Is sessionToken already in local storage at this point? Probably not here since I don't want you to have to refactor this all but I think we might as well just pull it from local storage when we can. (I'm not sure we should have ever passed it as part of signin state heh)
| signinState.sessionToken | ||
| ); | ||
| // TODO verify that recoveryPhoneGet returns a masked phone number (last four digits only) | ||
| phoneNumber && setLastFourPhoneDigits(phoneNumber.slice(-4)); |
There was a problem hiding this comment.
You might could create a little helper function for this:
fxa/packages/fxa-settings/src/components/Settings/SubRow/index.tsx
Lines 240 to 241 in 9955924
See:
fxa/libs/accounts/recovery-phone/src/lib/recovery-phone.service.ts
Lines 192 to 193 in 9955924
fxa/libs/accounts/recovery-phone/src/lib/recovery-phone.service.ts
Lines 259 to 260 in 9955924
There was a problem hiding this comment.
Would be nice to be more consistent. Could we clean this up in the ticket you filled for the twilio national number format?
There was a problem hiding this comment.
I added a note in FXA-11044
| return; | ||
| } | ||
|
|
||
| if (phoneNumber && (!count || count === 0)) { |
There was a problem hiding this comment.
Is the type of this null | number? Not super nice that any of these returned values from authClient are any 😅
| }, [authClient, lastFourPhoneDigits, signinState, navigateWithQuery]); | ||
|
|
||
| const handlePhoneChoice = async () => { | ||
| if (!signinState) { |
There was a problem hiding this comment.
If you want to use the session token from signin state, you could curry this function with it so you don't have to recheck if (!signinState) {, but maybe later we should just pull from local storage instead and curry that.
There was a problem hiding this comment.
I added a note in FXA-9875 about reviewing what's included in signinState (vs local storage)
| } | ||
|
|
||
| if (!signinState || !lastFourPhoneDigits) { | ||
| return <LoadingSpinner fullScreen />; |
There was a problem hiding this comment.
Is this where the redirect should happen instead of useEffect?
| user.type(screen.getByRole('textbox'), MOCK_RECOVERY_CODE) | ||
| ); | ||
|
|
||
| expect(screen.getByRole('button', { name: /Confirm/i })).toHaveAttribute( |
| @@ -0,0 +1,99 @@ | |||
| import React from 'react'; | |||
| React.useState(false); | ||
| const ftlMsgResolver = useFtlMsgResolver(); | ||
|
|
||
| const maskedPhoneNumber = `+1-•••-${lastFourPhoneDigits}`; |
There was a problem hiding this comment.
You may want to update this to just the dots, for consistency across add/delete inside Settings. In that new ticket I referenced, I think we can iron this out with that national_format from Twilio if we want these formatted properly.
Because: * New feature: recovery phone as recovery method for 2FA during sign in This commit: * Hook up new pages to choose recovery method and use recovery phone during sign in Closes #FXA-10374

Because
This pull request
Issue that this pull request solves
Closes: #FXA-10374
Checklist
Put an
xin the boxes that applyScreenshots (Optional)
Please attach the screenshots of the changes made in case of change in user interface.
Other information (Optional)
Notes for testing - options to manually test the flow until #18276 is merged: either manually add a recovery phone to an existing account with 2FA via mysql or cherry pick the latest commit for PR-18276 to use UI to add a phone number
generated codes can be retrieved with redis-commander (or cherry pick #18277 to use inbox logs)