1
1
import { CaptchaChallenge } from '../utils/captcha/CaptchaChallenge' ;
2
2
import type { Clerk } from './resources/internal' ;
3
- import { Client , isClerkAPIResponseError } from './resources/internal' ;
3
+ import { ClerkRuntimeError , Client , isClerkAPIResponseError } from './resources/internal' ;
4
4
5
5
export class FraudProtection {
6
6
private static instance : FraudProtection ;
7
7
8
8
private inflightException : Promise < unknown > | null = null ;
9
9
10
+ private captchaRetryCount = 0 ;
11
+ private readonly MAX_RETRY_ATTEMPTS = 3 ;
12
+
10
13
public static getInstance ( ) : FraudProtection {
11
14
if ( ! FraudProtection . instance ) {
12
15
FraudProtection . instance = new FraudProtection ( Client , CaptchaChallenge ) ;
@@ -20,6 +23,13 @@ export class FraudProtection {
20
23
) { }
21
24
22
25
public async execute < T extends ( ) => Promise < any > , R = Awaited < ReturnType < T > > > ( clerk : Clerk , cb : T ) : Promise < R > {
26
+ if ( this . captchaAttemptsExceeded ( ) ) {
27
+ throw new ClerkRuntimeError (
28
+ 'Security verification failed. Please try again by refreshing the page, clearing your browser cookies, or using a different web browser.' ,
29
+ { code : 'captcha_client_attempts_exceeded' } ,
30
+ ) ;
31
+ }
32
+
23
33
try {
24
34
if ( this . inflightException ) {
25
35
await this . inflightException ;
@@ -47,10 +57,15 @@ export class FraudProtection {
47
57
let resolve : any ;
48
58
this . inflightException = new Promise < unknown > ( r => ( resolve = r ) ) ;
49
59
50
- const captchaParams = await this . managedChallenge ( clerk ) ;
51
-
52
60
try {
53
- await this . client . getOrCreateInstance ( ) . sendCaptchaToken ( captchaParams ) ;
61
+ const captchaParams : any = await this . managedChallenge ( clerk ) ;
62
+ if ( captchaParams ?. captchaError !== 'modal_component_not_ready' ) {
63
+ await this . client . getOrCreateInstance ( ) . sendCaptchaToken ( captchaParams ) ;
64
+ this . captchaRetryCount = 0 ; // Reset the retry count on success
65
+ }
66
+ } catch ( err ) {
67
+ this . captchaRetryCount ++ ;
68
+ throw err ;
54
69
} finally {
55
70
// Resolve the exception placeholder promise so that other exceptions can be handled
56
71
resolve ( ) ;
@@ -64,4 +79,8 @@ export class FraudProtection {
64
79
public managedChallenge ( clerk : Clerk ) {
65
80
return new this . CaptchaChallengeImpl ( clerk ) . managedInModal ( { action : 'verify' } ) ;
66
81
}
82
+
83
+ private captchaAttemptsExceeded = ( ) => {
84
+ return this . captchaRetryCount >= this . MAX_RETRY_ATTEMPTS ;
85
+ } ;
67
86
}
0 commit comments