Skip to content
Merged
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/yellow-cycles-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Stop falling back to the Clerk proxy worker if turnstile fails to load as it is not as accurate as challenges.cloudflare.com
10 changes: 4 additions & 6 deletions packages/clerk-js/src/utils/captcha/CaptchaChallenge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ export class CaptchaChallenge {
* always use the fallback key.
*/
public async invisible() {
const { captchaSiteKey, canUseCaptcha, captchaURL, captchaPublicKeyInvisible } = retrieveCaptchaInfo(this.clerk);
const { captchaSiteKey, canUseCaptcha, captchaPublicKeyInvisible } = retrieveCaptchaInfo(this.clerk);

if (canUseCaptcha && captchaSiteKey && captchaURL && captchaPublicKeyInvisible) {
if (canUseCaptcha && captchaSiteKey && captchaPublicKeyInvisible) {
return getCaptchaToken({
siteKey: captchaPublicKeyInvisible,
invisibleSiteKey: captchaPublicKeyInvisible,
widgetType: 'invisible',
scriptUrl: captchaURL,
captchaProvider: 'turnstile',
}).catch(e => {
if (e.captchaError) {
Expand All @@ -41,15 +40,14 @@ export class CaptchaChallenge {
* Managed challenged start as non-interactive and escalate to interactive if necessary.
*/
public async managedOrInvisible(opts?: Partial<CaptchaOptions>) {
const { captchaSiteKey, canUseCaptcha, captchaURL, captchaWidgetType, captchaProvider, captchaPublicKeyInvisible } =
const { captchaSiteKey, canUseCaptcha, captchaWidgetType, captchaProvider, captchaPublicKeyInvisible } =
retrieveCaptchaInfo(this.clerk);

if (canUseCaptcha && captchaSiteKey && captchaURL && captchaPublicKeyInvisible) {
if (canUseCaptcha && captchaSiteKey && captchaPublicKeyInvisible) {
return getCaptchaToken({
siteKey: captchaSiteKey,
widgetType: captchaWidgetType,
invisibleSiteKey: captchaPublicKeyInvisible,
scriptUrl: captchaURL,
captchaProvider,
...opts,
}).catch(e => {
Expand Down
9 changes: 0 additions & 9 deletions packages/clerk-js/src/utils/captcha/retrieveCaptchaInfo.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type { Clerk } from '../../core/clerk';
import { createFapiClient } from '../../core/fapiClient';

export const retrieveCaptchaInfo = (clerk: Clerk) => {
const _environment = clerk.__unstable__environment;
const fapiClient = createFapiClient(clerk);
const captchaProvider = _environment ? _environment.displayConfig.captchaProvider : 'turnstile';

return {
Expand All @@ -12,12 +10,5 @@ export const retrieveCaptchaInfo = (clerk: Clerk) => {
captchaProvider,
captchaPublicKeyInvisible: _environment ? _environment.displayConfig.captchaPublicKeyInvisible : null,
canUseCaptcha: _environment ? _environment.userSettings.signUp.captcha_enabled && clerk.isStandardBrowser : null,
captchaURL: fapiClient
.buildUrl({
path: 'cloudflare/turnstile/v0/api.js',
pathPrefix: '',
search: '?render=explicit',
})
.toString(),
};
};
24 changes: 6 additions & 18 deletions packages/clerk-js/src/utils/captcha/turnstile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,11 @@ export const shouldRetryTurnstileErrorCode = (errorCode: string) => {
return !!codesWithRetries.find(w => errorCode.startsWith(w));
};

async function loadCaptcha(fallbackUrl: string) {
async function loadCaptcha() {
if (!window.turnstile) {
await loadCaptchaFromCloudflareURL()
.catch(() => loadCaptchaFromFAPIProxiedURL(fallbackUrl))
.catch(() => {
throw { captchaError: 'captcha_script_failed_to_load' };
});
await loadCaptchaFromCloudflareURL().catch(() => {
throw { captchaError: 'captcha_script_failed_to_load' };
});
}
return window.turnstile;
}
Expand All @@ -100,16 +98,6 @@ async function loadCaptchaFromCloudflareURL() {
}
}

async function loadCaptchaFromFAPIProxiedURL(fallbackUrl: string) {
try {
return await loadScript(fallbackUrl, { defer: true });
} catch (err) {
// Rethrow with specific message
console.error('Clerk: Failed to load the CAPTCHA script from the URL: ', fallbackUrl);
throw err;
}
}

/*
* How this function works:
* The widgetType is either 'invisible' or 'smart'.
Expand All @@ -118,9 +106,9 @@ async function loadCaptchaFromFAPIProxiedURL(fallbackUrl: string) {
* not exist, the invisibleSiteKey is used as a fallback and the widget is rendered in a hidden div at the bottom of the body.
*/
export const getTurnstileToken = async (opts: CaptchaOptions) => {
const { siteKey, scriptUrl, widgetType, invisibleSiteKey } = opts;
const { siteKey, widgetType, invisibleSiteKey } = opts;
const { modalContainerQuerySelector, modalWrapperQuerySelector, closeModal, openModal } = opts;
const captcha: Turnstile = await loadCaptcha(scriptUrl);
const captcha: Turnstile = await loadCaptcha();
const errorCodes: (string | number)[] = [];

let captchaToken = '';
Expand Down
1 change: 0 additions & 1 deletion packages/clerk-js/src/utils/captcha/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { CaptchaProvider, CaptchaWidgetType } from '@clerk/types';

export type CaptchaOptions = {
siteKey: string;
scriptUrl: string;
widgetType: CaptchaWidgetType;
invisibleSiteKey: string;
captchaProvider: CaptchaProvider;
Expand Down
Loading