Preliminary Checks
Reproduction
No hosted reproduction. The behaviour is in the prebuilt <UserProfile /> and <SignIn /> components themselves, and needs no custom code. Every source line is linked below. You can reproduce it in any app that renders <UserProfile /> on an instance with Authenticator application and SMS verification code MFA enabled; the steps are at the end.
Publishable key
Not provided. The problem isn't specific to our instance, and it shows up on any instance with the MFA settings above.
Description
In the MFA section of the prebuilt <UserProfile />, the "Set as default" action on an SMS second factor behaves inconsistently. Below, we separate what we observed at runtime from what we confirmed only by reading Clerk's source. We did not create a test instance or test user to execute the source-confirmed parts.
1. Runtime-confirmed: "Set as default" doesn't launch reverification, and shows the raw error instead
Observed: a signed-in user opens <UserProfile /> → Security and chooses ⋯ → Set as default on an SMS MFA phone number. The card shows the raw error:
You need to provide additional verification to perform this operation.
No reverification modal opens, the action isn't retried, and the user is stuck. We saw this in production with clerk-js 5.127.2 and an unmodified <UserProfile /> (only two appearance classes passed; no custom pages or localization).
Cause (source-confirmed): the action calls makeDefaultSecondFactor() directly, without useReverification:
As far as we can tell, the other sensitive mutations in UserProfile/ are wrapped in useReverification: creating phone, email, TOTP, backup codes and passkeys; changing the primary email/phone; changing the password; revoking a session; deleting the user; and the removals via RemoveResourceForm. So this looks like an omission.
2. Source-confirmed: with an authenticator app enrolled, the authenticator is always "Default", yet SMS rows keep offering "Set as default"
:35: showTOTP = secondFactors.includes('totp') && user.totpEnabled
:72: the authenticator-app row always renders the badge__default ("Default") badge.
:88: const isDefault = !showTOTP && phone.defaultSecondFactor;. While TOTP is enrolled, no SMS row can show "Default", whatever its defaultSecondFactor value.
:172–175: "Set as default" is offered whenever !isDefault. So every SMS row keeps offering it, including after the action has succeeded.
3. Source-confirmed: prebuilt sign-in always starts on TOTP before phone code, so defaultSecondFactor doesn't make SMS the first second-factor screen
So for a user with an authenticator app and a verified SMS factor, from the source: even when "Set as default" succeeds (default_second_factor: true), the badge stays on the authenticator app and sign-in still opens on the authenticator-code step. As we read it, defaultSecondFactor only orders phone numbers against each other, and nothing in the UI says so. The PhoneNumber reference describes makeDefaultSecondFactor() as marking the number as the default second factor for MFA, which suggests a stronger effect.
4. Questions and requests
Is this intentional?
- If yes: please don't show "Set as default" on SMS rows as if it changes the user's preferred MFA method. For example, hide it while an authenticator app is enrolled, or explain what it does.
- If not:
- wrap
makeDefaultSecondFactor() in useReverification, so the user gets the reverification flow and the action retries;
- make the displayed "Default" state consistent with the user's actual default second factor;
- have the prebuilt
<SignIn /> (and the reverification modal) honour the user's preferred/default second factor, or provide a supported configuration for SMS-vs-TOTP priority.
Steps to reproduce (development instance, test user)
- Enable MFA with Authenticator application and SMS verification code; keep the default reverification window.
- Sign up a test user, enrol an authenticator app, then add and verify an SMS second factor.
- Open
<UserProfile /> → Security. The authenticator app shows Default, and the SMS row's ⋯ menu shows Set as default.
- Item 1: once the session is outside the reverification window, choose ⋯ → Set as default on the SMS row.
- Expected: the reverification modal opens, and the action retries.
- Actual (as observed by us): the raw error above.
- Items 2–3: right after a fresh sign-in, choose ⋯ → Set as default on the SMS row, then sign out and back in.
- Expected: SMS shows Default, and sign-in starts with SMS; or the action isn't offered.
- Expected per the source (not executed by us): the request succeeds, but the badge stays on the authenticator app, the action is still offered, and sign-in starts on the authenticator-code step.
Environment
@clerk/clerk-js (loaded in the browser): 5.127.2 (the same code is on v5 latest and on main e34a5cc262 / packages/ui)
@clerk/nextjs: 6.39.4
@clerk/clerk-react: 5.61.7
@clerk/shared: 3.47.6
Components: prebuilt <UserProfile /> and <SignIn />, no customisation beyond appearance classes
Preliminary Checks
Reproduction
No hosted reproduction. The behaviour is in the prebuilt
<UserProfile />and<SignIn />components themselves, and needs no custom code. Every source line is linked below. You can reproduce it in any app that renders<UserProfile />on an instance with Authenticator application and SMS verification code MFA enabled; the steps are at the end.Publishable key
Not provided. The problem isn't specific to our instance, and it shows up on any instance with the MFA settings above.
Description
In the MFA section of the prebuilt
<UserProfile />, the "Set as default" action on an SMS second factor behaves inconsistently. Below, we separate what we observed at runtime from what we confirmed only by reading Clerk's source. We did not create a test instance or test user to execute the source-confirmed parts.1. Runtime-confirmed: "Set as default" doesn't launch reverification, and shows the raw error instead
Observed: a signed-in user opens
<UserProfile />→ Security and chooses ⋯ → Set as default on an SMS MFA phone number. The card shows the raw error:No reverification modal opens, the action isn't retried, and the user is stuck. We saw this in production with clerk-js 5.127.2 and an unmodified
<UserProfile />(only two appearance classes passed; no custom pages or localization).Cause (source-confirmed): the action calls
makeDefaultSecondFactor()directly, withoutuseReverification:MfaSection.tsx:175@ clerk-js 5.127.2:onClick: () => phone.makeDefaultSecondFactor().catch(err => handleError(err, [], card.setError))main:packages/ui/.../MfaSection.tsx:175As far as we can tell, the other sensitive mutations in
UserProfile/are wrapped inuseReverification: creating phone, email, TOTP, backup codes and passkeys; changing the primary email/phone; changing the password; revoking a session; deleting the user; and the removals viaRemoveResourceForm. So this looks like an omission.2. Source-confirmed: with an authenticator app enrolled, the authenticator is always "Default", yet SMS rows keep offering "Set as default"
:35:showTOTP = secondFactors.includes('totp') && user.totpEnabled:72: the authenticator-app row always renders thebadge__default("Default") badge.:88:const isDefault = !showTOTP && phone.defaultSecondFactor;. While TOTP is enrolled, no SMS row can show "Default", whatever itsdefaultSecondFactorvalue.:172–175: "Set as default" is offered whenever!isDefault. So every SMS row keeps offering it, including after the action has succeeded.3. Source-confirmed: prebuilt sign-in always starts on TOTP before phone code, so
defaultSecondFactordoesn't make SMS the first second-factor screenSignIn/utils.ts:99–115@ 5.127.2 (main:packages/ui/.../SignIn/utils.ts:100):determineStartingSignInSecondFactorreturns thetotpfactor whenever one exists, and only then falls back tophone_code. It doesn't consultdefaultSecondFactor.UserVerificationFactorTwo.tsx:49).So for a user with an authenticator app and a verified SMS factor, from the source: even when "Set as default" succeeds (
default_second_factor: true), the badge stays on the authenticator app and sign-in still opens on the authenticator-code step. As we read it,defaultSecondFactoronly orders phone numbers against each other, and nothing in the UI says so. ThePhoneNumberreference describesmakeDefaultSecondFactor()as marking the number as the default second factor for MFA, which suggests a stronger effect.4. Questions and requests
Is this intentional?
makeDefaultSecondFactor()inuseReverification, so the user gets the reverification flow and the action retries;<SignIn />(and the reverification modal) honour the user's preferred/default second factor, or provide a supported configuration for SMS-vs-TOTP priority.Steps to reproduce (development instance, test user)
<UserProfile />→ Security. The authenticator app shows Default, and the SMS row's ⋯ menu shows Set as default.Environment