From 781c7bd7458de074e9302ab8a43d671190f8c1d7 Mon Sep 17 00:00:00 2001 From: Alex Bratsos Date: Tue, 15 Jul 2025 19:11:53 +0200 Subject: [PATCH 1/5] fix(clerk-js): show error message when account is locked --- .../src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx | 1 + .../src/ui/components/SignIn/__tests__/SignInFactorOne.test.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx index 0faac0ed1a4..bb2100733b5 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx @@ -83,6 +83,7 @@ export const SignInFactorOnePasswordCard = (props: SignInFactorOnePasswordProps) }) .catch(err => { if (isUserLockedError(err)) { + card.setError(err.errors[0]); // @ts-expect-error -- private method for the time being return clerk.__internal_navigateWithError('..', err.errors[0]); } diff --git a/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInFactorOne.test.tsx b/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInFactorOne.test.tsx index ccb475182e4..0b7a61bab7f 100644 --- a/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInFactorOne.test.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInFactorOne.test.tsx @@ -223,6 +223,7 @@ describe('SignInFactorOne', () => { await userEvent.type(screen.getByLabelText('Password'), '123456'); await userEvent.click(screen.getByText('Continue')); await waitFor(() => { + expect(screen.getByText('Your account is locked. Please try again after 1 hour.')).toBeDefined(); expect(fixtures.clerk.__internal_navigateWithError).toHaveBeenCalledWith('..', parseError(errJSON)); }); }); From 0606d0e17653bcd1f46e1fcbd50abcbe7251f9d3 Mon Sep 17 00:00:00 2001 From: Alex Bratsos Date: Wed, 16 Jul 2025 19:14:27 +0200 Subject: [PATCH 2/5] chore: add changeset --- .changeset/soft-garlics-wonder.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/soft-garlics-wonder.md diff --git a/.changeset/soft-garlics-wonder.md b/.changeset/soft-garlics-wonder.md new file mode 100644 index 00000000000..1e0a62e04de --- /dev/null +++ b/.changeset/soft-garlics-wonder.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Adds missing error message when an account is locked in hash routing mode. From 5d0777e3f60693440b6f66a0ce4bfb96ff8cbc57 Mon Sep 17 00:00:00 2001 From: Alex Bratsos Date: Thu, 17 Jul 2025 20:56:29 +0200 Subject: [PATCH 3/5] fixup! fix(clerk-js): show error message when account is locked --- .../components/SignIn/SignInFactorOnePasswordCard.tsx | 1 - packages/clerk-js/src/ui/elements/contexts/index.tsx | 11 ++++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx index bb2100733b5..0faac0ed1a4 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx @@ -83,7 +83,6 @@ export const SignInFactorOnePasswordCard = (props: SignInFactorOnePasswordProps) }) .catch(err => { if (isUserLockedError(err)) { - card.setError(err.errors[0]); // @ts-expect-error -- private method for the time being return clerk.__internal_navigateWithError('..', err.errors[0]); } diff --git a/packages/clerk-js/src/ui/elements/contexts/index.tsx b/packages/clerk-js/src/ui/elements/contexts/index.tsx index c91fb670ff1..19e903343d9 100644 --- a/packages/clerk-js/src/ui/elements/contexts/index.tsx +++ b/packages/clerk-js/src/ui/elements/contexts/index.tsx @@ -4,7 +4,6 @@ import { FloatingTree, useFloatingParentNodeId } from '@floating-ui/react'; import React from 'react'; import { useLocalizations } from '../../customizables'; -import { useSafeState } from '../../hooks'; type Status = 'idle' | 'loading' | 'error'; type Metadata = string | undefined; @@ -19,11 +18,17 @@ const [CardStateCtx, _useCardState] = createContextAndHook('C export const CardStateProvider = (props: React.PropsWithChildren) => { const { translateError } = useLocalizations(); - const [state, setState] = useSafeState({ + const [state, setState] = React.useState(() => ({ status: 'idle', metadata: undefined, error: translateError(window?.Clerk?.__internal_last_error || undefined), - }); + })); + + const lastError = window?.Clerk?.__internal_last_error; + if (lastError) { + console.log('CardStateProvider: setting error', lastError); + setState(s => ({ ...s, error: translateError(lastError) })); + } const value = React.useMemo(() => ({ value: { state, setState } }), [state, setState]); return {props.children}; From 15d3d655efa43856fd767b044ab46d4d700b188a Mon Sep 17 00:00:00 2001 From: Alex Bratsos Date: Fri, 18 Jul 2025 12:57:39 +0200 Subject: [PATCH 4/5] fixup! fix(clerk-js): show error message when account is locked --- packages/clerk-js/src/core/clerk.ts | 1 + packages/clerk-js/src/core/events.ts | 2 ++ .../SignIn/__tests__/SignInFactorOne.test.tsx | 1 - .../src/ui/elements/contexts/index.tsx | 33 ++++++++++++++----- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 8d1f5450c43..fc915966a58 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -2216,6 +2216,7 @@ export class Clerk implements ClerkInterface { __internal_navigateWithError(to: string, err: ClerkAPIError) { this.__internal_last_error = err; + eventBus.emit(events.ErrorUserLocked, null); return this.navigate(to); } diff --git a/packages/clerk-js/src/core/events.ts b/packages/clerk-js/src/core/events.ts index 55f1d8996ab..bc0cbf5b2af 100644 --- a/packages/clerk-js/src/core/events.ts +++ b/packages/clerk-js/src/core/events.ts @@ -6,6 +6,7 @@ export const events = { UserSignOut: 'user:signOut', EnvironmentUpdate: 'environment:update', SessionTokenResolved: 'session:tokenResolved', + ErrorUserLocked: 'error:user_locked', } as const; type TokenUpdatePayload = { token: TokenResource | null }; @@ -15,6 +16,7 @@ type InternalEvents = { [events.UserSignOut]: null; [events.EnvironmentUpdate]: null; [events.SessionTokenResolved]: null; + [events.ErrorUserLocked]: null; }; export const eventBus = createEventBus(); diff --git a/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInFactorOne.test.tsx b/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInFactorOne.test.tsx index 0b7a61bab7f..ccb475182e4 100644 --- a/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInFactorOne.test.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInFactorOne.test.tsx @@ -223,7 +223,6 @@ describe('SignInFactorOne', () => { await userEvent.type(screen.getByLabelText('Password'), '123456'); await userEvent.click(screen.getByText('Continue')); await waitFor(() => { - expect(screen.getByText('Your account is locked. Please try again after 1 hour.')).toBeDefined(); expect(fixtures.clerk.__internal_navigateWithError).toHaveBeenCalledWith('..', parseError(errJSON)); }); }); diff --git a/packages/clerk-js/src/ui/elements/contexts/index.tsx b/packages/clerk-js/src/ui/elements/contexts/index.tsx index 19e903343d9..1a96044757c 100644 --- a/packages/clerk-js/src/ui/elements/contexts/index.tsx +++ b/packages/clerk-js/src/ui/elements/contexts/index.tsx @@ -3,6 +3,9 @@ import type { ClerkAPIError, ClerkRuntimeError } from '@clerk/types'; import { FloatingTree, useFloatingParentNodeId } from '@floating-ui/react'; import React from 'react'; +import { useSafeState } from '@/ui/hooks'; + +import { eventBus, events } from '../../../core/events'; import { useLocalizations } from '../../customizables'; type Status = 'idle' | 'loading' | 'error'; @@ -18,17 +21,29 @@ const [CardStateCtx, _useCardState] = createContextAndHook('C export const CardStateProvider = (props: React.PropsWithChildren) => { const { translateError } = useLocalizations(); - const [state, setState] = React.useState(() => ({ + const [state, setState] = useSafeState({ status: 'idle', metadata: undefined, - error: translateError(window?.Clerk?.__internal_last_error || undefined), - })); - - const lastError = window?.Clerk?.__internal_last_error; - if (lastError) { - console.log('CardStateProvider: setting error', lastError); - setState(s => ({ ...s, error: translateError(lastError) })); - } + error: undefined, + }); + + React.useEffect(() => { + const initialError = window?.Clerk?.__internal_last_error; + if (initialError) { + setState(s => ({ ...s, error: translateError(initialError) })); + } + + const handler = () => { + const error = window?.Clerk?.__internal_last_error; + if (error) { + setState(s => ({ ...s, error: translateError(error) })); + } + }; + + eventBus.on(events.ErrorUserLocked, handler); + + return () => eventBus.off(events.ErrorUserLocked, handler); + }, [translateError, setState]); const value = React.useMemo(() => ({ value: { state, setState } }), [state, setState]); return {props.children}; From c0c1c29acbbed331a7e7a8e2806f3013ac050d14 Mon Sep 17 00:00:00 2001 From: Alex Bratsos Date: Mon, 21 Jul 2025 17:58:21 +0200 Subject: [PATCH 5/5] fixup! fix(clerk-js): show error message when account is locked --- packages/clerk-js/src/core/clerk.ts | 1 - packages/clerk-js/src/core/events.ts | 2 -- .../src/ui/elements/contexts/index.tsx | 30 +++++++------------ 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index fc915966a58..8d1f5450c43 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -2216,7 +2216,6 @@ export class Clerk implements ClerkInterface { __internal_navigateWithError(to: string, err: ClerkAPIError) { this.__internal_last_error = err; - eventBus.emit(events.ErrorUserLocked, null); return this.navigate(to); } diff --git a/packages/clerk-js/src/core/events.ts b/packages/clerk-js/src/core/events.ts index bc0cbf5b2af..55f1d8996ab 100644 --- a/packages/clerk-js/src/core/events.ts +++ b/packages/clerk-js/src/core/events.ts @@ -6,7 +6,6 @@ export const events = { UserSignOut: 'user:signOut', EnvironmentUpdate: 'environment:update', SessionTokenResolved: 'session:tokenResolved', - ErrorUserLocked: 'error:user_locked', } as const; type TokenUpdatePayload = { token: TokenResource | null }; @@ -16,7 +15,6 @@ type InternalEvents = { [events.UserSignOut]: null; [events.EnvironmentUpdate]: null; [events.SessionTokenResolved]: null; - [events.ErrorUserLocked]: null; }; export const eventBus = createEventBus(); diff --git a/packages/clerk-js/src/ui/elements/contexts/index.tsx b/packages/clerk-js/src/ui/elements/contexts/index.tsx index 1a96044757c..7204a7a14d2 100644 --- a/packages/clerk-js/src/ui/elements/contexts/index.tsx +++ b/packages/clerk-js/src/ui/elements/contexts/index.tsx @@ -3,9 +3,8 @@ import type { ClerkAPIError, ClerkRuntimeError } from '@clerk/types'; import { FloatingTree, useFloatingParentNodeId } from '@floating-ui/react'; import React from 'react'; -import { useSafeState } from '@/ui/hooks'; +import { useRouter } from '@/ui/router'; -import { eventBus, events } from '../../../core/events'; import { useLocalizations } from '../../customizables'; type Status = 'idle' | 'loading' | 'error'; @@ -20,30 +19,21 @@ const [CardStateCtx, _useCardState] = createContextAndHook('C export const CardStateProvider = (props: React.PropsWithChildren) => { const { translateError } = useLocalizations(); + const router = useRouter(); - const [state, setState] = useSafeState({ + const [state, setState] = React.useState(() => ({ status: 'idle', metadata: undefined, - error: undefined, - }); + error: translateError(window?.Clerk?.__internal_last_error || undefined), + })); React.useEffect(() => { - const initialError = window?.Clerk?.__internal_last_error; - if (initialError) { - setState(s => ({ ...s, error: translateError(initialError) })); - } - - const handler = () => { - const error = window?.Clerk?.__internal_last_error; - if (error) { - setState(s => ({ ...s, error: translateError(error) })); - } - }; + const error = window?.Clerk?.__internal_last_error; - eventBus.on(events.ErrorUserLocked, handler); - - return () => eventBus.off(events.ErrorUserLocked, handler); - }, [translateError, setState]); + if (error) { + setState(s => ({ ...s, error: translateError(error) })); + } + }, [translateError, setState, router.currentPath]); const value = React.useMemo(() => ({ value: { state, setState } }), [state, setState]); return {props.children};