diff --git a/.changeset/green-eggs-kiss.md b/.changeset/green-eggs-kiss.md
new file mode 100644
index 00000000000..f2bd8a39f01
--- /dev/null
+++ b/.changeset/green-eggs-kiss.md
@@ -0,0 +1,5 @@
+---
+'@clerk/clerk-js': patch
+---
+
+Fix Smart CAPTCHA on ticket flow
diff --git a/packages/clerk-js/src/ui/elements/LoadingCard.tsx b/packages/clerk-js/src/ui/elements/LoadingCard.tsx
index 5c099f0b46d..462e76200df 100644
--- a/packages/clerk-js/src/ui/elements/LoadingCard.tsx
+++ b/packages/clerk-js/src/ui/elements/LoadingCard.tsx
@@ -1,6 +1,7 @@
import type { PropsWithChildren } from 'react';
import { descriptors, Flex, Spinner } from '../customizables';
+import { CaptchaElement } from './CaptchaElement';
import { Card } from './Card';
import { useCardState, withCardStateProvider } from './contexts';
@@ -33,6 +34,7 @@ export const LoadingCard = withCardStateProvider(() => {
{card.error}
+
diff --git a/packages/clerk-js/src/utils/captcha.ts b/packages/clerk-js/src/utils/captcha.ts
index c6b77624bfd..370c2308754 100644
--- a/packages/clerk-js/src/utils/captcha.ts
+++ b/packages/clerk-js/src/utils/captcha.ts
@@ -100,7 +100,7 @@ export const getCaptchaToken = async (captchaOptions: {
const { siteKey, scriptUrl, widgetType, invisibleSiteKey } = captchaOptions;
let captchaToken = '',
id = '';
- let invisibleWidget = !widgetType || widgetType === 'invisible';
+ let isInvisibleWidget = !widgetType || widgetType === 'invisible';
let turnstileSiteKey = siteKey;
let widgetDiv: HTMLElement | null = null;
@@ -112,21 +112,6 @@ export const getCaptchaToken = async (captchaOptions: {
return div;
};
- if (invisibleWidget) {
- widgetDiv = createInvisibleDOMElement();
- } else {
- const visibleDiv = document.getElementById(CAPTCHA_ELEMENT_ID);
- if (visibleDiv) {
- visibleDiv.style.display = 'block';
- widgetDiv = visibleDiv;
- } else {
- console.error('Captcha DOM element not found. Using invisible captcha widget.');
- widgetDiv = createInvisibleDOMElement();
- invisibleWidget = true;
- turnstileSiteKey = invisibleSiteKey;
- }
- }
-
const captcha = await loadCaptcha(scriptUrl);
let retries = 0;
const errorCodes: (string | number)[] = [];
@@ -134,7 +119,22 @@ export const getCaptchaToken = async (captchaOptions: {
const handleCaptchaTokenGeneration = (): Promise<[string, string]> => {
return new Promise((resolve, reject) => {
try {
- const id = captcha.render(invisibleWidget ? `.${CAPTCHA_INVISIBLE_CLASSNAME}` : `#${CAPTCHA_ELEMENT_ID}`, {
+ if (isInvisibleWidget) {
+ widgetDiv = createInvisibleDOMElement();
+ } else {
+ const visibleDiv = document.getElementById(CAPTCHA_ELEMENT_ID);
+ if (visibleDiv) {
+ visibleDiv.style.display = 'block';
+ widgetDiv = visibleDiv;
+ } else {
+ console.error('Captcha DOM element not found. Using invisible captcha widget.');
+ widgetDiv = createInvisibleDOMElement();
+ isInvisibleWidget = true;
+ turnstileSiteKey = invisibleSiteKey;
+ }
+ }
+
+ const id = captcha.render(isInvisibleWidget ? `.${CAPTCHA_INVISIBLE_CLASSNAME}` : `#${CAPTCHA_ELEMENT_ID}`, {
sitekey: turnstileSiteKey,
appearance: 'interaction-only',
retry: 'never',
@@ -186,12 +186,14 @@ export const getCaptchaToken = async (captchaOptions: {
captchaError: e,
};
} finally {
- if (invisibleWidget) {
- document.body.removeChild(widgetDiv);
- } else {
- widgetDiv.style.display = 'none';
+ if (widgetDiv) {
+ if (isInvisibleWidget) {
+ document.body.removeChild(widgetDiv as HTMLElement);
+ } else {
+ (widgetDiv as HTMLElement).style.display = 'none';
+ }
}
}
- return { captchaToken, captchaWidgetTypeUsed: invisibleWidget ? 'invisible' : 'smart' };
+ return { captchaToken, captchaWidgetTypeUsed: isInvisibleWidget ? 'invisible' : 'smart' };
};