From 6f71a36d9772ca9f97abf9e1a65ad3617b7b1345 Mon Sep 17 00:00:00 2001 From: paulogdm Date: Mon, 31 Aug 2026 14:07:53 -0300 Subject: [PATCH 01/10] feat(ui): add mobile deep-link for authenticator app QR setup A QR code shown on a mobile device can't be scanned by that same device's camera, leaving mobile users with no smooth way to link an authenticator app during two-step verification setup. Add a mobile-only "Open in authenticator app" action alongside the QR code that opens the otpauth:// URI directly, in both the UserProfile self-service MFA setup and the SessionTasks forced setup-MFA task. Co-Authored-By: Claude Sonnet 5 --- .../mfa-totp-mobile-authenticator-link.md | 7 +++++++ packages/localizations/src/en-US.ts | 2 ++ packages/shared/src/types/localization.ts | 2 ++ .../tasks/TaskSetupMfa/TOTPCodeFlowScreen.tsx | 21 +++++++++++++++---- .../UserProfile/AddAuthenticatorApp.tsx | 21 +++++++++++++++---- 5 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 .changeset/mfa-totp-mobile-authenticator-link.md diff --git a/.changeset/mfa-totp-mobile-authenticator-link.md b/.changeset/mfa-totp-mobile-authenticator-link.md new file mode 100644 index 00000000000..c8425f85789 --- /dev/null +++ b/.changeset/mfa-totp-mobile-authenticator-link.md @@ -0,0 +1,7 @@ +--- +'@clerk/ui': patch +'@clerk/localizations': patch +'@clerk/shared': patch +--- + +When setting up an authenticator app for two-step verification on a mobile device, an "Open in authenticator app" link now appears alongside the QR code, since a QR code shown on the same phone can't be scanned by that phone's camera. diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 2126dc5cbef..34d2035f441 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -1729,6 +1729,7 @@ export const enUS: LocalizationResource = { totpCode: { addAuthenticatorApp: { buttonAbleToScan__nonPrimary: 'Scan QR code instead', + buttonOpenInAuthenticatorApp__nonPrimary: 'Open in authenticator app', buttonUnableToScan__nonPrimary: "Can't scan QR code?", formButtonPrimary: 'Continue', formButtonReset: 'Cancel', @@ -2078,6 +2079,7 @@ export const enUS: LocalizationResource = { mfaTOTPPage: { authenticatorApp: { buttonAbleToScan__nonPrimary: 'Scan QR code instead', + buttonOpenInAuthenticatorApp__nonPrimary: 'Open in authenticator app', buttonUnableToScan__nonPrimary: 'Can’t scan QR code?', infoText__ableToScan: 'Set up a new sign-in method in your authenticator app and scan the following QR code to link it to your account.', diff --git a/packages/shared/src/types/localization.ts b/packages/shared/src/types/localization.ts index 82c326a2248..970850b6109 100644 --- a/packages/shared/src/types/localization.ts +++ b/packages/shared/src/types/localization.ts @@ -927,6 +927,7 @@ export type __internal_LocalizationResource = { inputLabel__unableToScan2: LocalizationValue; buttonAbleToScan__nonPrimary: LocalizationValue; buttonUnableToScan__nonPrimary: LocalizationValue; + buttonOpenInAuthenticatorApp__nonPrimary?: LocalizationValue; }; removeResource: { title: LocalizationValue; @@ -2097,6 +2098,7 @@ export type __internal_LocalizationResource = { inputLabel__unableToScan1: LocalizationValue; buttonUnableToScan__nonPrimary: LocalizationValue; buttonAbleToScan__nonPrimary: LocalizationValue; + buttonOpenInAuthenticatorApp__nonPrimary?: LocalizationValue; formButtonPrimary: LocalizationValue; formButtonReset: LocalizationValue; }; diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/TOTPCodeFlowScreen.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/TOTPCodeFlowScreen.tsx index 9bee1a91f7f..b9149ae55a5 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/TOTPCodeFlowScreen.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/TOTPCodeFlowScreen.tsx @@ -16,6 +16,7 @@ import { Checkmark, Clipboard } from '@/icons'; import { Button, Col, descriptors, localizationKeys, Text } from '@/ui/customizables'; import { Card } from '@/ui/elements/Card'; import { SuccessPage } from '@/ui/elements/SuccessPage'; +import { isMobileDevice } from '@/ui/utils/isMobileDevice'; import { handleError } from '@/utils/errorHandler'; import { SharedFooterActionForSignOut } from './shared'; @@ -78,10 +79,22 @@ export const AddAuthenticatorApp = withCardStateProvider((props: AddAuthenticato <> {displayFormat === 'qr' && ( - + <> + + {isMobileDevice() && totp.uri && ( +