Skip to content

Commit

Permalink
feat(clerk-js,types): Handle factor-one verification on sign-in (#1629)
Browse files Browse the repository at this point in the history
  • Loading branch information
kostaspt authored Sep 4, 2023
1 parent bed5313 commit 463ff84
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .changeset/gentle-carpets-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': minor
'@clerk/types': minor
---

Introduced a new `firstFactorUrl` property in sign-in callback to handle unverified emails.
34 changes: 34 additions & 0 deletions packages/clerk-js/src/core/clerk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,40 @@ describe('Clerk singleton', () => {
expect(mockNavigate).toHaveBeenCalledWith('/sign-up#/verify-email-address');
});
});

it('redirects user to first-factor if the email is claimed but the external account has an unverified email', async () => {
mockEnvironmentFetch.mockReturnValue(
Promise.resolve({
authConfig: {},
userSettings: mockUserSettings,
displayConfig: mockDisplayConfig,
isSingleSession: () => false,
isProduction: () => false,
isDevelopmentOrStaging: () => true,
}),
);

mockClientFetch.mockReturnValue(
Promise.resolve({
activeSessions: [],
signIn: new SignIn({
status: 'needs_first_factor',
} as unknown as SignInJSON),
signUp: new SignUp(null),
}),
);

const sut = new Clerk(frontendApi);
await sut.load({
navigate: mockNavigate,
});

await sut.handleRedirectCallback();

await waitFor(() => {
expect(mockNavigate).toHaveBeenCalledWith('/sign-in#/factor-one');
});
});
});

describe('.handleMagicLinkVerification()', () => {
Expand Down
11 changes: 11 additions & 0 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,11 @@ export default class Clerk implements ClerkInterface {

const navigateToSignUp = makeNavigate(displayConfig.signUpUrl);

const navigateToFactorOne = makeNavigate(
params.firstFactorUrl ||
buildURL({ base: displayConfig.signInUrl, hashPath: '/factor-one' }, { stringify: true }),
);

const navigateToFactorTwo = makeNavigate(
params.secondFactorUrl ||
buildURL({ base: displayConfig.signInUrl, hashPath: '/factor-two' }, { stringify: true }),
Expand Down Expand Up @@ -913,6 +918,12 @@ export default class Clerk implements ClerkInterface {
}
}

const userHasUnverifiedEmail = si.status === 'needs_first_factor';

if (userHasUnverifiedEmail) {
return navigateToFactorOne();
}

const userNeedsToBeCreated = si.firstFactorVerificationStatus === 'transferable';

if (userNeedsToBeCreated) {
Expand Down
1 change: 1 addition & 0 deletions packages/clerk-js/src/ui/components/SignIn/SignIn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ function SignInRoutes(): JSX.Element {
afterSignUpUrl={signInContext.afterSignUpUrl}
redirectUrl={signInContext.redirectUrl}
continueSignUpUrl={signInContext.signUpContinueUrl}
firstFactorUrl={'../factor-one'}
secondFactorUrl={'../factor-two'}
/>
</Route>
Expand Down
6 changes: 6 additions & 0 deletions packages/types/src/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,12 @@ export type HandleOAuthCallbackParams = {
*/
redirectUrl?: string | null;

/**
* Full URL or path to navigate during sign in,
* if identifier verification is required.
*/
firstFactorUrl?: string;

/**
* Full URL or path to navigate during sign in,
* if 2FA is enabled.
Expand Down

0 comments on commit 463ff84

Please sign in to comment.