Storybooks for commit 0230eb9d902cc5534712f199d0c09e6fd3b0ac41

Date
8/6/2026, 11:27:52 AM UTC
Summary
fix(auth-server): never return the full recovery phone number
Description
Because:
* GET /account is gated only by a bare sessionToken, with no tokenVerified
  or assurance-level check. A caller holding nothing but the account
  password therefore received the unmasked E.164 number in
  recoveryPhone.phoneNumber, which is the credential needed to mount a SIM
  swap and defeat TOTP.
* GET /recovery_phone decided whether to mask from emailVerified,
  mustVerify and tokenVerified, and never consulted the session's
  assurance level, so a session that cleared those checks without reaching
  AAL2 also received the full number.

This commit:
* Adds RecoveryPhoneService.hasConfirmedMasked(), returning only the last
  four digits, and points both read endpoints at it. Masking is
  unconditional rather than derived from the caller's assurance level, so
  there is no gate left to get wrong.
* Omits nationalFormat from both read responses. Twilio's grouping would
  reveal the number's length and region, and clients render the last four
  through the localized recovery-phone-number-ending-digits string rather
  than formatting them, so the formatted value has no consumer.
* Drops the phoneNumberStrip parameter from hasConfirmed(). Defaulting it
  to "do not mask" is what made the leak possible. The raw accessor is now
  documented server-side only and is reached by the email, setup and
  change paths, which need a dialable number.
* Constrains phoneNumber in both response schemas through a new
  maskedPhoneNumber validator, and drops nationalFormat from them
  entirely, so a future regression fails response validation instead of
  shipping.
* Routes PageRecoveryPhoneRemove through formatPhoneNumber, which
  otherwise rendered a bare "1234" now that no national format arrives.

Settings previously showed a client-masked national format and now shows
"Number ending in 1234", matching what the signin and reset-password
recovery screens already displayed. The display is consistent, localized
and safe to render in RTL locales.

The confirm and change endpoints still return a dialable number. Both are
gated on a verified session or mfa:2fa and echo a number the caller just
supplied and proved control of, so they are not an egress path.

Closes FXA-14028