Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .changeset/deep-mails-exist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { isUserLockedError } from '@clerk/shared/error';
import { clerkInvalidFAPIResponse } from '@clerk/shared/internal/clerk-js/errors';
import { useClerk } from '@clerk/shared/react';
import type { PhoneCodeFactor, SignInFactor } from '@clerk/shared/types';

import { useCardState } from '@/ui/elements/contexts';
import type { VerificationCodeCardProps } from '@/ui/elements/VerificationCodeCard';
import { VerificationCodeCard } from '@/ui/elements/VerificationCodeCard';
import { handleError } from '@/ui/utils/errorHandler';

import { useCoreSignIn, useSignInContext } from '../../contexts';
import { useSupportEmail } from '../../hooks/useSupportEmail';
import { useCoreSignIn } from '../../contexts';
import { type LocalizationKey, localizationKeys } from '../../localization';
import { useRouter } from '../../router';
import { useHandleFirstFactorResult, useHandleUserLockedError } from './useHandleAttemptResult';

export type SignInFactorOneAlternativeChannelCodeCard = Pick<
VerificationCodeCardProps,
Expand All @@ -34,10 +31,8 @@ export const SignInFactorOneAlternativeChannelCodeForm = (props: SignInFactorOne
const signIn = useCoreSignIn();
const card = useCardState();
const { navigate } = useRouter();
const { afterSignInUrl, navigateOnSetActive } = useSignInContext();
const { setActive } = useClerk();
const supportEmail = useSupportEmail();
const clerk = useClerk();
const handleFirstFactorResult = useHandleFirstFactorResult();
const handleUserLockedError = useHandleUserLockedError();
const channel = props.factor.channel;

const shouldAvoidPrepare = signIn.firstFactorVerification.status === 'verified' && props.factorAlreadyPrepared;
Expand All @@ -62,29 +57,12 @@ export const SignInFactorOneAlternativeChannelCodeForm = (props: SignInFactorOne
.attemptFirstFactor({ strategy: props.factor.strategy, code })
.then(async res => {
await resolve();

switch (res.status) {
case 'complete':
return setActive({
session: res.createdSessionId,
navigate: async ({ session, decorateUrl }) => {
await navigateOnSetActive({ session, redirectUrl: afterSignInUrl, decorateUrl });
},
});
case 'needs_second_factor':
return navigate('../factor-two');
case 'needs_new_password':
return navigate('../reset-password');
default:
return console.error(clerkInvalidFAPIResponse(res.status, supportEmail));
}
return handleFirstFactorResult(res);
})
.catch(err => {
if (isUserLockedError(err)) {
// @ts-expect-error -- private method for the time being
return clerk.__internal_navigateWithError('..', err.errors[0]);
if (handleUserLockedError(err)) {
return;
}

return reject(err);
});
};
Expand Down
36 changes: 7 additions & 29 deletions packages/ui/src/components/SignIn/SignInFactorOneCodeForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import { isUserLockedError } from '@clerk/shared/error';
import { clerkInvalidFAPIResponse } from '@clerk/shared/internal/clerk-js/errors';
import { useClerk } from '@clerk/shared/react';
import type { EmailCodeFactor, PhoneCodeFactor, ResetPasswordCodeFactor } from '@clerk/shared/types';
import { useMemo } from 'react';

Expand All @@ -9,11 +6,11 @@ import type { VerificationCodeCardProps } from '@/ui/elements/VerificationCodeCa
import { VerificationCodeCard } from '@/ui/elements/VerificationCodeCard';
import { handleError } from '@/ui/utils/errorHandler';

import { useCoreSignIn, useSignInContext } from '../../contexts';
import { useCoreSignIn } from '../../contexts';
import { useFetch } from '../../hooks';
import { useSupportEmail } from '../../hooks/useSupportEmail';
import { type LocalizationKey } from '../../localization';
import { useRouter } from '../../router';
import { useHandleFirstFactorResult, useHandleUserLockedError } from './useHandleAttemptResult';

export type SignInFactorOneCodeCard = Pick<
VerificationCodeCardProps,
Expand All @@ -35,10 +32,8 @@ export const SignInFactorOneCodeForm = (props: SignInFactorOneCodeFormProps) =>
const signIn = useCoreSignIn();
const card = useCardState();
const { navigate } = useRouter();
const { afterSignInUrl, navigateOnSetActive } = useSignInContext();
const { setActive } = useClerk();
const supportEmail = useSupportEmail();
const clerk = useClerk();
const handleFirstFactorResult = useHandleFirstFactorResult();
const handleUserLockedError = useHandleUserLockedError();

const shouldAvoidPrepare = signIn.firstFactorVerification.status === 'verified' && props.factorAlreadyPrepared;

Expand Down Expand Up @@ -93,29 +88,12 @@ export const SignInFactorOneCodeForm = (props: SignInFactorOneCodeFormProps) =>
.attemptFirstFactor({ strategy: props.factor.strategy, code })
.then(async res => {
await resolve();

switch (res.status) {
case 'complete':
return setActive({
session: res.createdSessionId,
navigate: async ({ session, decorateUrl }) => {
await navigateOnSetActive({ session, redirectUrl: afterSignInUrl, decorateUrl });
},
});
case 'needs_second_factor':
return navigate('../factor-two');
case 'needs_new_password':
return navigate('../reset-password');
default:
return console.error(clerkInvalidFAPIResponse(res.status, supportEmail));
}
return handleFirstFactorResult(res);
})
.catch(err => {
if (isUserLockedError(err)) {
// @ts-expect-error -- private method for the time being
return clerk.__internal_navigateWithError('..', err.errors[0]);
if (handleUserLockedError(err)) {
return;
}

return reject(err);
});
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { isUserLockedError } from '@clerk/shared/error';
import { useClerk } from '@clerk/shared/react';
import type { EmailLinkFactor, SignInResource } from '@clerk/shared/types';
import React from 'react';
Expand All @@ -14,6 +13,7 @@ import { Flow, localizationKeys, useLocalizations } from '../../customizables';
import { useCardState } from '../../elements/contexts';
import { useEmailLink } from '../../hooks/useEmailLink';
import { useRouter } from '../../router/RouteContext';
import { useHandleUserLockedError } from './useHandleAttemptResult';

type SignInFactorOneEmailLinkCardProps = Pick<VerificationCodeCardProps, 'onShowAlternativeMethodsClicked'> & {
factor: EmailLinkFactor;
Expand All @@ -32,7 +32,7 @@ export const SignInFactorOneEmailLinkCard = (props: SignInFactorOneEmailLinkCard
const { setActive } = useClerk();
const { startEmailLinkFlow, cancelEmailLinkFlow } = useEmailLink(signIn);
const [showVerifyModal, setShowVerifyModal] = React.useState(false);
const clerk = useClerk();
const handleUserLockedError = useHandleUserLockedError();

React.useEffect(() => {
void startEmailLinkVerification();
Expand All @@ -50,11 +50,9 @@ export const SignInFactorOneEmailLinkCard = (props: SignInFactorOneEmailLinkCard
})
.then(res => handleVerificationResult(res))
.catch(err => {
if (isUserLockedError(err)) {
// @ts-expect-error -- private method for the time being
return clerk.__internal_navigateWithError('..', err.errors[0]);
if (handleUserLockedError(err)) {
return;
}

handleError(err, [], card.setError);
});
};
Expand Down
37 changes: 8 additions & 29 deletions packages/ui/src/components/SignIn/SignInFactorOnePasswordCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { isPasswordCompromisedError, isPasswordPwnedError, isUserLockedError } from '@clerk/shared/error';
import { clerkInvalidFAPIResponse } from '@clerk/shared/internal/clerk-js/errors';
import { useClerk } from '@clerk/shared/react';
import { isPasswordCompromisedError, isPasswordPwnedError } from '@clerk/shared/error';
import React from 'react';

import { Card } from '@/ui/elements/Card';
Expand All @@ -11,11 +9,11 @@ import { IdentityPreview } from '@/ui/elements/IdentityPreview';
import { handleError } from '@/ui/utils/errorHandler';
import { useFormControl } from '@/ui/utils/useFormControl';

import { useCoreSignIn, useSignInContext } from '../../contexts';
import { useCoreSignIn } from '../../contexts';
import { descriptors, Flex, Flow, localizationKeys } from '../../customizables';
import { useSupportEmail } from '../../hooks/useSupportEmail';
import { useRouter } from '../../router/RouteContext';
import { HavingTrouble } from './HavingTrouble';
import { useHandleFirstFactorResult, useHandleUserLockedError } from './useHandleAttemptResult';
import { useResetPasswordFactor } from './useResetPasswordFactor';

export type PasswordErrorCode = 'compromised' | 'pwned';
Expand Down Expand Up @@ -55,15 +53,13 @@ export const SignInFactorOnePasswordCard = (props: SignInFactorOnePasswordProps)
const { onShowAlternativeMethodsClick, onPasswordError } = props;
const passwordInputRef = React.useRef<HTMLInputElement>(null);
const card = useCardState();
const { setActive } = useClerk();
const signIn = useCoreSignIn();
const { afterSignInUrl, navigateOnSetActive } = useSignInContext();
const supportEmail = useSupportEmail();
const passwordControl = usePasswordControl(props);
const { navigate } = useRouter();
const [showHavingTrouble, setShowHavingTrouble] = React.useState(false);
const toggleHavingTrouble = React.useCallback(() => setShowHavingTrouble(s => !s), [setShowHavingTrouble]);
const clerk = useClerk();
const handleFirstFactorResult = useHandleFirstFactorResult();
const handleUserLockedError = useHandleUserLockedError();

const goBack = () => {
void navigate('../');
Expand All @@ -73,27 +69,10 @@ export const SignInFactorOnePasswordCard = (props: SignInFactorOnePasswordProps)
e.preventDefault();
void signIn
.attemptFirstFactor({ strategy: 'password', password: passwordControl.value })
.then(res => {
switch (res.status) {
case 'complete':
return setActive({
session: res.createdSessionId,
navigate: ({ session, decorateUrl }) => {
return navigateOnSetActive({ session, redirectUrl: afterSignInUrl, decorateUrl });
},
});
case 'needs_second_factor':
return navigate('../factor-two');
case 'needs_client_trust':
return navigate('../client-trust');
default:
return console.error(clerkInvalidFAPIResponse(res.status, supportEmail));
}
})
.then(handleFirstFactorResult)
.catch(err => {
if (isUserLockedError(err)) {
// @ts-expect-error -- private method for the time being
return clerk.__internal_navigateWithError('..', err.errors[0]);
if (handleUserLockedError(err)) {
return;
}

if (onPasswordError) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import { isUserLockedError } from '@clerk/shared/error';
import { clerkInvalidFAPIResponse } from '@clerk/shared/internal/clerk-js/errors';
import { useClerk } from '@clerk/shared/react';
import type { SignInResource } from '@clerk/shared/types';
import React from 'react';

Expand All @@ -11,10 +8,9 @@ import { Header } from '@/ui/elements/Header';
import { handleError } from '@/ui/utils/errorHandler';
import { useFormControl } from '@/ui/utils/useFormControl';

import { useCoreSignIn, useSignInContext } from '../../contexts';
import { useCoreSignIn } from '../../contexts';
import { Col, descriptors, localizationKeys } from '../../customizables';
import { useSupportEmail } from '../../hooks/useSupportEmail';
import { useRouter } from '../../router';
import { useHandleSecondFactorResult, useHandleUserLockedError } from './useHandleAttemptResult';
import { isResetPasswordStrategy } from './utils';

type SignInFactorTwoBackupCodeCardProps = {
Expand All @@ -24,17 +20,14 @@ type SignInFactorTwoBackupCodeCardProps = {
export const SignInFactorTwoBackupCodeCard = (props: SignInFactorTwoBackupCodeCardProps) => {
const { onShowAlternativeMethodsClicked } = props;
const signIn = useCoreSignIn();
const { afterSignInUrl, navigateOnSetActive } = useSignInContext();
const { setActive } = useClerk();
const { navigate } = useRouter();
const supportEmail = useSupportEmail();
const card = useCardState();
const codeControl = useFormControl('code', '', {
type: 'text',
label: localizationKeys('formFieldLabel__backupCode'),
isRequired: true,
});
const clerk = useClerk();
const handleSecondFactorResult = useHandleSecondFactorResult();
const handleUserLockedError = useHandleUserLockedError();

const isResettingPassword = (resource: SignInResource) =>
isResetPasswordStrategy(resource.firstFactorVerification?.strategy) &&
Expand All @@ -44,30 +37,11 @@ export const SignInFactorTwoBackupCodeCard = (props: SignInFactorTwoBackupCodeCa
e.preventDefault();
return signIn
.attemptSecondFactor({ strategy: 'backup_code', code: codeControl.value })
.then(res => {
switch (res.status) {
case 'complete':
if (isResettingPassword(res) && res.createdSessionId) {
const queryParams = new URLSearchParams();
queryParams.set('createdSessionId', res.createdSessionId);
return navigate(`../reset-password-success?${queryParams.toString()}`);
}
return setActive({
session: res.createdSessionId,
navigate: async ({ session, decorateUrl }) => {
await navigateOnSetActive({ session, redirectUrl: afterSignInUrl, decorateUrl });
},
});
default:
return console.error(clerkInvalidFAPIResponse(res.status, supportEmail));
}
})
.then(handleSecondFactorResult)
.catch(err => {
if (isUserLockedError(err)) {
// @ts-expect-error -- private method for the time being
return clerk.__internal_navigateWithError('..', err.errors[0]);
if (handleUserLockedError(err)) {
return;
}

handleError(err, [codeControl], card.setError);
});
};
Expand Down
44 changes: 9 additions & 35 deletions packages/ui/src/components/SignIn/SignInFactorTwoCodeForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import { isUserLockedError } from '@clerk/shared/error';
import { clerkInvalidFAPIResponse } from '@clerk/shared/internal/clerk-js/errors';
import { useClerk } from '@clerk/shared/react';
import type { EmailCodeFactor, PhoneCodeFactor, SignInResource, TOTPFactor } from '@clerk/shared/types';
import React, { useMemo } from 'react';

Expand All @@ -9,11 +6,10 @@ import type { VerificationCodeCardProps } from '@/ui/elements/VerificationCodeCa
import { VerificationCodeCard } from '@/ui/elements/VerificationCodeCard';
import { handleError } from '@/ui/utils/errorHandler';

import { useCoreSignIn, useEnvironment, useSignInContext } from '../../contexts';
import { useCoreSignIn, useEnvironment } from '../../contexts';
import { localizationKeys, Text } from '../../customizables';
import { useSupportEmail } from '../../hooks/useSupportEmail';
import type { LocalizationKey } from '../../localization';
import { useRouter } from '../../router';
import { useHandleSecondFactorResult, useHandleUserLockedError } from './useHandleAttemptResult';
import { isResetPasswordStrategy } from './utils';

export type SignInFactorTwoCodeCard = Pick<VerificationCodeCardProps, 'onShowAlternativeMethodsClicked'> & {
Expand All @@ -39,11 +35,8 @@ export const SignInFactorTwoCodeForm = (props: SignInFactorTwoCodeFormProps) =>
const env = useEnvironment();
const signIn = useCoreSignIn();
const card = useCardState();
const { afterSignInUrl, navigateOnSetActive } = useSignInContext();
const { setActive } = useClerk();
const { navigate } = useRouter();
const supportEmail = useSupportEmail();
const clerk = useClerk();
const handleSecondFactorResult = useHandleSecondFactorResult();
const handleUserLockedError = useHandleUserLockedError();

// Only show the new device verification notice if the user is new
// and no attributes are explicitly used for second factor.
Expand All @@ -69,11 +62,9 @@ export const SignInFactorTwoCodeForm = (props: SignInFactorTwoCodeFormProps) =>
.prepare?.()
.then(() => props.onFactorPrepare())
.catch(err => {
if (isUserLockedError(err)) {
// @ts-expect-error -- private method for the time being
return clerk.__internal_navigateWithError('..', err.errors[0]);
if (handleUserLockedError(err)) {
return;
}

handleError(err, [], card.setError);
});
}
Expand All @@ -84,29 +75,12 @@ export const SignInFactorTwoCodeForm = (props: SignInFactorTwoCodeFormProps) =>
.attemptSecondFactor({ strategy: props.factor.strategy, code })
.then(async res => {
await resolve();
switch (res.status) {
case 'complete':
if (isResettingPassword(res) && res.createdSessionId) {
const queryParams = new URLSearchParams();
queryParams.set('createdSessionId', res.createdSessionId);
return navigate(`../reset-password-success?${queryParams.toString()}`);
}
return setActive({
session: res.createdSessionId,
navigate: async ({ session, decorateUrl }) => {
await navigateOnSetActive({ session, redirectUrl: afterSignInUrl, decorateUrl });
},
});
default:
return console.error(clerkInvalidFAPIResponse(res.status, supportEmail));
}
return handleSecondFactorResult(res);
})
.catch(err => {
if (isUserLockedError(err)) {
// @ts-expect-error -- private method for the time being
return clerk.__internal_navigateWithError('..', err.errors[0]);
if (handleUserLockedError(err)) {
return;
}

return reject(err);
});
};
Expand Down
Loading
Loading