Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/pretty-queens-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/ui': patch
---

Skip the strategy selection screen if only one MFA strategy is available for the setup MFA session task
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ describe('TaskSetupMFA', () => {
identifier: 'test@clerk.com',
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

const { queryByText, queryByRole } = render(<TaskSetupMFA />, { wrapper });
Expand Down Expand Up @@ -60,7 +62,7 @@ describe('TaskSetupMFA', () => {
expect(getByRole('button', { name: /sms code/i })).toBeInTheDocument();
});

it('should render SMS code item on the first screen if only SMS code is enabled', async () => {
it('should skip selection screen and go directly to SMS code flow when only SMS code is enabled', async () => {
const { wrapper } = await createFixtures(f => {
f.withUser({
email_addresses: ['test@clerk.com'],
Expand All @@ -70,14 +72,14 @@ describe('TaskSetupMFA', () => {
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

const { getByRole, queryByRole } = render(<TaskSetupMFA />, { wrapper });
const { findByText, queryByText } = render(<TaskSetupMFA />, { wrapper });

expect(queryByRole('button', { name: /authenticator application/i })).not.toBeInTheDocument();
expect(getByRole('button', { name: /sms code/i })).toBeInTheDocument();
await findByText(/add phone number/i);
expect(queryByText(/set up two-step verification/i)).not.toBeInTheDocument();
});

it('should render TOTP item on the first screen if only TOTP is enabled', async () => {
const { wrapper } = await createFixtures(f => {
it('should skip selection screen and go directly to TOTP flow when only TOTP is enabled', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withUser({
email_addresses: ['test@clerk.com'],
identifier: 'test@clerk.com',
Expand All @@ -86,10 +88,15 @@ describe('TaskSetupMFA', () => {
f.withAuthenticatorApp({ enabled: true });
});

const { getByRole, queryByRole } = render(<TaskSetupMFA />, { wrapper });
fixtures.clerk.user?.createTOTP.mockResolvedValue({
uri: 'otpauth://totp/Test:test@clerk.com?secret=TESTSECRET&issuer=Test',
secret: 'TESTSECRET',
} as TOTPResource);

const { findByText, queryByText } = render(<TaskSetupMFA />, { wrapper });

expect(getByRole('button', { name: /authenticator application/i })).toBeInTheDocument();
expect(queryByRole('button', { name: /sms code/i })).not.toBeInTheDocument();
await findByText(/add authenticator application/i);
expect(queryByText(/set up two-step verification/i)).not.toBeInTheDocument();
});
});
describe('authenticator application', () => {
Expand All @@ -101,6 +108,7 @@ describe('TaskSetupMFA', () => {
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

fixtures.clerk.user?.createTOTP.mockResolvedValue({
Expand All @@ -127,6 +135,7 @@ describe('TaskSetupMFA', () => {
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

fixtures.clerk.user?.createTOTP.mockResolvedValue({
Expand Down Expand Up @@ -159,6 +168,7 @@ describe('TaskSetupMFA', () => {
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

fixtures.clerk.user?.createTOTP.mockResolvedValue({
Expand Down Expand Up @@ -190,6 +200,7 @@ describe('TaskSetupMFA', () => {
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
f.withBackupCode();
});

Expand Down Expand Up @@ -234,6 +245,7 @@ describe('TaskSetupMFA', () => {
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

fixtures.clerk.user?.createTOTP.mockResolvedValue({
Expand Down Expand Up @@ -275,6 +287,7 @@ describe('TaskSetupMFA', () => {
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

fixtures.clerk.user?.createTOTP.mockResolvedValue({
Expand Down Expand Up @@ -351,6 +364,7 @@ describe('TaskSetupMFA', () => {
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

fixtures.clerk.user?.createTOTP.mockRejectedValueOnce(
Expand Down Expand Up @@ -384,6 +398,7 @@ describe('TaskSetupMFA', () => {
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

fixtures.clerk.user?.createTOTP.mockResolvedValue({
Expand Down Expand Up @@ -438,6 +453,7 @@ describe('TaskSetupMFA', () => {
phone_numbers: [{ phone_number: '+306911111111', id: 'phone_1' }],
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

Expand All @@ -460,6 +476,7 @@ describe('TaskSetupMFA', () => {
identifier: 'test@clerk.com',
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

Expand All @@ -481,6 +498,7 @@ describe('TaskSetupMFA', () => {
phone_numbers: [{ phone_number: '+306911111111', id: 'phone_1' }],
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

Expand Down Expand Up @@ -514,6 +532,7 @@ describe('TaskSetupMFA', () => {
],
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
f.withBackupCode();
});
Expand Down Expand Up @@ -556,6 +575,7 @@ describe('TaskSetupMFA', () => {
],
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

Expand Down Expand Up @@ -602,6 +622,7 @@ describe('TaskSetupMFA', () => {
],
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

Expand Down Expand Up @@ -662,6 +683,7 @@ describe('TaskSetupMFA', () => {
],
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

Expand Down Expand Up @@ -705,6 +727,7 @@ describe('TaskSetupMFA', () => {
identifier: 'test@clerk.com',
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

Expand Down Expand Up @@ -746,6 +769,7 @@ describe('TaskSetupMFA', () => {
identifier: 'test@clerk.com',
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

Expand Down Expand Up @@ -794,6 +818,7 @@ describe('TaskSetupMFA', () => {
],
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

Expand Down Expand Up @@ -846,6 +871,7 @@ describe('TaskSetupMFA', () => {
],
tasks: [{ key: 'setup-mfa' }],
});
f.withAuthenticatorApp({ enabled: true });
f.withPhoneNumber({ second_factors: ['phone_code'], used_for_second_factor: true });
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import { SetupMfaStartScreen } from './SetupMfaStartScreen';
import { SmsCodeFlow } from './SmsCodeFlowScreen';
import { TOTPCodeFlow } from './TOTPCodeFlowScreen';

const WIZARD_STEPS = {
start: 0,
phoneCode: 1,
totp: 2,
} as const;

const TaskSetupMFAInternal = () => {
const clerk = useClerk();
const { user } = useUser();
Expand All @@ -38,7 +44,11 @@ const TaskSetupMFAInternal = () => {
});
}, [attributes, user]);

const wizard = useWizard({ defaultStep: 0 });
const defaultStep =
secondFactorsAvailableToAdd.indexOf('phone_code') > -1 ? WIZARD_STEPS.phoneCode : WIZARD_STEPS.totp;
const wizard = useWizard({
defaultStep: secondFactorsAvailableToAdd.length > 1 ? WIZARD_STEPS.start : defaultStep,
});
const { redirectUrlComplete } = useTaskSetupMFAContext();
const { navigateOnSetActive, redirectOnActiveSession } = useSessionTasksContext();

Expand Down
Loading