-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[No QA] [ECUK In-App 3DS] Add essential actions, wrappers for the auth keys, challenges logic & BIOMETRICS_TEST scenario
#79477
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
rafecolton
merged 12 commits into
Expensify:main
from
software-mansion-labs:korytko/ecuk/3ds/add-actions-and-wrappers-for-auth-and-challenges
Jan 26, 2026
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
acb9f51
chore: add biometrics test scenario config & actions
JakubKorytko 175b5a3
chore: add MFA auth keys & challenge wrappers
JakubKorytko e5df7c6
tests: add biometrics test scenario config & actions
JakubKorytko a5dccda
tests: add MFA auth keys & challenge wrappers
JakubKorytko f321b99
fix: address PR comments pt.1
JakubKorytko 450020f
fix: address PR comments pt.2
JakubKorytko f850566
fix: address PR comments pt.3
JakubKorytko 0e4a793
refractor: rename MFA notifications to outcomes
JakubKorytko 2548bc7
fix: backend error codes
JakubKorytko 896d75d
Merge branch 'main' into korytko/ecuk/3ds/add-actions-and-wrappers-fo…
dariusz-biela eb0109e
docs: add comment explaining ESLint disable for numeric keys
dariusz-biela b6ea455
fix: correct authentication method mappings for Marqeta 3DS
dariusz-biela File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -756,6 +756,7 @@ | |
| "Warchoł", | ||
| "WDYR", | ||
| "webapps", | ||
| "webauthn", | ||
| "webcredentials", | ||
| "webrtc", | ||
| "welldone", | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| /** | ||
| * Configuration exports for multifactor authentication UI components and scenarios. | ||
| */ | ||
| import mapMultifactorAuthenticationOutcomes from './mapMultifactorAuthenticationOutcomes'; | ||
| import MULTIFACTOR_AUTHENTICATION_SCENARIO_CONFIG from './scenarios'; | ||
|
|
||
| const MULTIFACTOR_AUTHENTICATION_OUTCOME_MAP = mapMultifactorAuthenticationOutcomes(MULTIFACTOR_AUTHENTICATION_SCENARIO_CONFIG); | ||
|
|
||
| export {MULTIFACTOR_AUTHENTICATION_SCENARIO_CONFIG, MULTIFACTOR_AUTHENTICATION_OUTCOME_MAP}; | ||
| export {default as MULTIFACTOR_AUTHENTICATION_PROMPT_UI} from './scenarios/prompts'; | ||
| export {default as MULTIFACTOR_AUTHENTICATION_DEFAULT_UI} from './scenarios/DefaultUserInterface'; | ||
| export type {Payloads as MultifactorAuthenticationScenarioPayload} from './scenarios'; |
97 changes: 97 additions & 0 deletions
97
src/components/MultifactorAuthentication/config/mapMultifactorAuthenticationOutcomes.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| import StringUtils from '@libs/StringUtils'; | ||
| import type { | ||
| MultifactorAuthenticationOutcomeMap, | ||
| MultifactorAuthenticationOutcomeOptions, | ||
| MultifactorAuthenticationOutcomeRecord, | ||
| MultifactorAuthenticationScenario, | ||
| MultifactorAuthenticationScenarioConfigRecord, | ||
| } from './types'; | ||
|
|
||
| /** | ||
| * This utility module provides functions to map multifactor authentication scenario configurations | ||
| * to an outcome map with kebab-case keys. | ||
| * | ||
| * This allows outcome pages to reference the config based on its OutcomeType in url. | ||
| * | ||
| * e.g. | ||
| * | ||
| * { | ||
| * "BIOMETRICS-TEST": { | ||
| * // ... | ||
| * OUTCOMES: { | ||
| * success: { | ||
| * title: "...", | ||
| * // ... | ||
| * }, | ||
| * failure: { | ||
| * title: "...", | ||
| * // ... | ||
| * }, | ||
| * // ... | ||
| * } | ||
| * }, | ||
| * "AUTHORIZE-TRANSACTION": { | ||
| * // ... | ||
| * } | ||
| * } | ||
| * | ||
| * is mapped to: | ||
| * | ||
| * { | ||
| * "biometrics-test-success": { | ||
| * title: "...", | ||
| * // ... | ||
| * }, | ||
| * "biometrics-test-failure": { | ||
| * title: "...", | ||
| * // ... | ||
| * }, | ||
| * "authorize-transaction-success": { | ||
| * // ... | ||
| * } | ||
| * // ... | ||
| * } | ||
| */ | ||
|
|
||
| /** | ||
| * Creates an outcome record from multifactor authentication scenario configuration. | ||
| * For details refer to the example above. | ||
| */ | ||
| const createOutcomeRecord = (mfaConfig: MultifactorAuthenticationScenarioConfigRecord): MultifactorAuthenticationOutcomeRecord => { | ||
| const entries = Object.entries({...mfaConfig}); | ||
| return entries.reduce((record, [key, {OUTCOMES}]) => { | ||
| // eslint-disable-next-line no-param-reassign | ||
| record[key as MultifactorAuthenticationScenario] = {...OUTCOMES}; | ||
| return record; | ||
| }, {} as MultifactorAuthenticationOutcomeRecord); | ||
| }; | ||
|
|
||
| /** | ||
| * Creates an outcome key by combining scenario and outcome name in kebab-case format. | ||
| * e.g. a scenario key of "BIOMETRICS-TEST" and outcome name of "success" will produce "biometrics-test-success". | ||
| */ | ||
| const createOutcomeKey = (key: string, name: string) => { | ||
| const scenarioKebabCase = StringUtils.toLowerCase(key as MultifactorAuthenticationScenario); | ||
| const outcomeName = StringUtils.camelToKebabCase(name as MultifactorAuthenticationOutcomeOptions); | ||
|
|
||
| return `${scenarioKebabCase}-${outcomeName}` as const; | ||
| }; | ||
|
|
||
| /** | ||
| * Maps multifactor authentication scenario configuration to an outcome map with kebab-case keys. | ||
| */ | ||
| const mapMultifactorAuthenticationOutcomes = (mfaConfig: MultifactorAuthenticationScenarioConfigRecord) => { | ||
| const recordEntries = Object.entries(createOutcomeRecord({...mfaConfig})); | ||
|
|
||
| const outcomes: Partial<MultifactorAuthenticationOutcomeMap> = {}; | ||
|
|
||
| for (const [key, config] of recordEntries) { | ||
| for (const [name, ui] of Object.entries(config)) { | ||
| outcomes[createOutcomeKey(key, name)] = {...ui}; | ||
| } | ||
| } | ||
|
|
||
| return outcomes as MultifactorAuthenticationOutcomeMap; | ||
| }; | ||
|
|
||
| export default mapMultifactorAuthenticationOutcomes; |
25 changes: 25 additions & 0 deletions
25
src/components/MultifactorAuthentication/config/scenarios/BiometricsTest.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import type {MultifactorAuthenticationScenarioCustomConfig} from '@components/MultifactorAuthentication/config/types'; | ||
| import {troubleshootMultifactorAuthentication} from '@userActions/MultifactorAuthentication'; | ||
| import CONST from '@src/CONST'; | ||
| import SCREENS from '@src/SCREENS'; | ||
|
|
||
| /** | ||
| * Configuration for the biometrics test multifactor authentication scenario. | ||
| */ | ||
| export default { | ||
| allowedAuthenticationMethods: [CONST.MULTIFACTOR_AUTHENTICATION.TYPE.BIOMETRICS], | ||
| action: troubleshootMultifactorAuthentication, | ||
| screen: SCREENS.MULTIFACTOR_AUTHENTICATION.BIOMETRICS_TEST, | ||
| pure: true, | ||
| OUTCOMES: { | ||
| success: { | ||
| headerTitle: 'multifactorAuthentication.biometricsTest.biometricsTest', | ||
| }, | ||
| failure: { | ||
| headerTitle: 'multifactorAuthentication.biometricsTest.biometricsTest', | ||
| }, | ||
| outOfTime: { | ||
| headerTitle: 'multifactorAuthentication.biometricsTest.biometricsTest', | ||
| }, | ||
JakubKorytko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
| } as const satisfies MultifactorAuthenticationScenarioCustomConfig; | ||
105 changes: 105 additions & 0 deletions
105
src/components/MultifactorAuthentication/config/scenarios/DefaultUserInterface.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| import type {MultifactorAuthenticationDefaultUIConfig, MultifactorAuthenticationScenarioCustomConfig} from '@components/MultifactorAuthentication/config/types'; | ||
| import NoEligibleMethodsDescription from '@components/MultifactorAuthentication/NoEligibleMethodsDescription'; | ||
| // Spacing utilities are needed for icon padding configuration in outcomes defaults | ||
| // eslint-disable-next-line no-restricted-imports | ||
| import spacing from '@styles/utils/spacing'; | ||
JakubKorytko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| import variables from '@styles/variables'; | ||
|
|
||
| /** | ||
| * Default UI configuration for all multifactor authentication scenarios with modals and outcomes. | ||
| */ | ||
| const DEFAULT_CONFIG = { | ||
| OUTCOMES: { | ||
| success: { | ||
| illustration: 'OpenPadlock', | ||
| iconWidth: variables.openPadlockWidth, | ||
| iconHeight: variables.openPadlockHeight, | ||
| padding: spacing.p2, | ||
| headerTitle: 'multifactorAuthentication.biometricsTest.biometricsAuthentication', | ||
| title: 'multifactorAuthentication.biometricsTest.authenticationSuccessful', | ||
| description: 'multifactorAuthentication.biometricsTest.successfullyAuthenticatedUsing', | ||
| }, | ||
| failure: { | ||
| illustration: 'HumptyDumpty', | ||
| iconWidth: variables.humptyDumptyWidth, | ||
| iconHeight: variables.humptyDumptyHeight, | ||
| padding: spacing.p0, | ||
| headerTitle: 'multifactorAuthentication.biometricsTest.biometricsAuthentication', | ||
| title: 'multifactorAuthentication.oops', | ||
| description: 'multifactorAuthentication.biometricsTest.yourAttemptWasUnsuccessful', | ||
| }, | ||
| outOfTime: { | ||
| illustration: 'RunOutOfTime', | ||
| iconWidth: variables.runOutOfTimeWidth, | ||
| iconHeight: variables.runOutOfTimeHeight, | ||
| padding: spacing.p0, | ||
| headerTitle: 'multifactorAuthentication.biometricsTest.biometricsAuthentication', | ||
| title: 'multifactorAuthentication.youRanOutOfTime', | ||
| description: 'multifactorAuthentication.looksLikeYouRanOutOfTime', | ||
| }, | ||
| noEligibleMethods: { | ||
| illustration: 'HumptyDumpty', | ||
| iconWidth: variables.humptyDumptyWidth, | ||
| iconHeight: variables.humptyDumptyHeight, | ||
| padding: spacing.p0, | ||
| headerTitle: 'multifactorAuthentication.biometricsTest.biometricsAuthentication', | ||
| title: 'multifactorAuthentication.biometricsTest.youCouldNotBeAuthenticated', | ||
| description: 'multifactorAuthentication.biometricsTest.youCouldNotBeAuthenticated', | ||
| customDescription: NoEligibleMethodsDescription, | ||
| }, | ||
| }, | ||
| MODALS: { | ||
| cancelConfirmation: { | ||
| title: 'common.areYouSure', | ||
| description: 'multifactorAuthentication.biometricsTest.areYouSureToReject', | ||
| confirmButtonText: 'multifactorAuthentication.biometricsTest.rejectAuthentication', | ||
| cancelButtonText: 'common.cancel', | ||
| }, | ||
| }, | ||
| nativePromptTitle: 'multifactorAuthentication.letsVerifyItsYou', | ||
| } as const satisfies MultifactorAuthenticationDefaultUIConfig; | ||
|
|
||
| /** | ||
| * Merges custom scenario configuration with default UI configuration for modals and outcomes. | ||
| */ | ||
| function customConfig<const T extends MultifactorAuthenticationScenarioCustomConfig<never>>(config: T) { | ||
| const MODALS = { | ||
| ...DEFAULT_CONFIG.MODALS, | ||
| ...config.MODALS, | ||
| cancelConfirmation: { | ||
| ...DEFAULT_CONFIG.MODALS.cancelConfirmation, | ||
| ...config.MODALS?.cancelConfirmation, | ||
| }, | ||
| } as const; | ||
|
|
||
| const OUTCOMES = { | ||
| ...DEFAULT_CONFIG.OUTCOMES, | ||
| ...config.OUTCOMES, | ||
| success: { | ||
| ...DEFAULT_CONFIG.OUTCOMES.success, | ||
| ...config.OUTCOMES?.success, | ||
| }, | ||
| failure: { | ||
| ...DEFAULT_CONFIG.OUTCOMES.failure, | ||
| ...config.OUTCOMES?.failure, | ||
| }, | ||
| outOfTime: { | ||
| ...DEFAULT_CONFIG.OUTCOMES.outOfTime, | ||
| ...config.OUTCOMES?.outOfTime, | ||
| }, | ||
| noEligibleMethods: { | ||
| ...DEFAULT_CONFIG.OUTCOMES.noEligibleMethods, | ||
| ...config.OUTCOMES?.noEligibleMethods, | ||
| }, | ||
| } as const; | ||
|
|
||
| return { | ||
| ...DEFAULT_CONFIG, | ||
| ...config, | ||
| MODALS, | ||
| OUTCOMES, | ||
| } as const; | ||
| } | ||
|
|
||
| export default DEFAULT_CONFIG; | ||
| export {customConfig}; | ||
28 changes: 28 additions & 0 deletions
28
src/components/MultifactorAuthentication/config/scenarios/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import type {EmptyObject} from 'type-fest'; | ||
| import type {MultifactorAuthenticationScenarioConfigRecord} from '@components/MultifactorAuthentication/config/types'; | ||
| import CONST from '@src/CONST'; | ||
| import BiometricsTest from './BiometricsTest'; | ||
| import {customConfig} from './DefaultUserInterface'; | ||
|
|
||
| /** | ||
| * Payload types for multifactor authentication scenarios. | ||
| * Since the BiometricsTest does not require any payload, it is an empty object for now. | ||
| * The AuthorizeTransaction Scenario will change it, as it needs the transactionID to be provided as well. | ||
| * | ||
| * { | ||
| * "AUTHORIZE-TRANSACTION": { | ||
| * transactionID: string; | ||
| * } | ||
| * } | ||
| */ | ||
| type Payloads = EmptyObject; | ||
JakubKorytko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Configuration records for all multifactor authentication scenarios. | ||
| */ | ||
| const Configs = { | ||
| [CONST.MULTIFACTOR_AUTHENTICATION.SCENARIO.BIOMETRICS_TEST]: customConfig(BiometricsTest), | ||
| } as const satisfies MultifactorAuthenticationScenarioConfigRecord; | ||
|
|
||
| export default Configs; | ||
| export type {Payloads}; | ||
20 changes: 20 additions & 0 deletions
20
src/components/MultifactorAuthentication/config/scenarios/names.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| /** | ||
| * Multifactor authentication scenario names. | ||
JakubKorytko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * | ||
| * The names need to be a kebab-case string to satisfy the requirements of the URL schema. | ||
| * Moreover, they are exported to a separate file to avoid circular dependencies | ||
| * as the Multifactor Authentication configs imports SCREENS, actions, and other shared modules, | ||
| * and at the same time the config is imported in the CONSTs. | ||
| */ | ||
| const SCENARIO_NAMES = { | ||
| BIOMETRICS_TEST: 'BIOMETRICS-TEST', | ||
| } as const; | ||
|
|
||
| /** | ||
| * Prompt identifiers for multifactor authentication scenarios. | ||
| */ | ||
| const PROMPT_NAMES = { | ||
| ENABLE_BIOMETRICS: 'enable-biometrics', | ||
| }; | ||
|
|
||
| export {SCENARIO_NAMES, PROMPT_NAMES}; | ||
15 changes: 15 additions & 0 deletions
15
src/components/MultifactorAuthentication/config/scenarios/prompts.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import LottieAnimations from '@components/LottieAnimations'; | ||
| import type {MultifactorAuthenticationPrompt} from '@components/MultifactorAuthentication/config/types'; | ||
| import VALUES from '@libs/MultifactorAuthentication/Biometrics/VALUES'; | ||
|
|
||
| /** | ||
| * Configuration for multifactor authentication prompt UI with animations and translations. | ||
JakubKorytko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * Exported to a separate file to avoid circular dependencies. | ||
| */ | ||
| export default { | ||
| [VALUES.PROMPT.ENABLE_BIOMETRICS]: { | ||
| animation: LottieAnimations.Fingerprint, | ||
| title: 'multifactorAuthentication.verifyYourself.biometrics', | ||
| subtitle: 'multifactorAuthentication.enableQuickVerification.biometrics', | ||
| }, | ||
| } as const satisfies MultifactorAuthenticationPrompt; | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.