11import { inBrowser } from '@clerk/shared/browser' ;
22import { type ClerkError , ClerkRuntimeError , isCaptchaError , isClerkAPIResponseError } from '@clerk/shared/error' ;
3+ import { PROTECT_CHECK_CONTAINER_ID } from '@clerk/shared/internal/clerk-js/constants' ;
34import { createValidatePassword } from '@clerk/shared/internal/clerk-js/passwords/password' ;
45import { windowNavigate } from '@clerk/shared/internal/clerk-js/windowNavigate' ;
56import { Poller } from '@clerk/shared/poller' ;
@@ -56,6 +57,7 @@ import {
5657} from '../../utils/authenticateWithPopup' ;
5758import { CaptchaChallenge } from '../../utils/captcha/CaptchaChallenge' ;
5859import { normalizeUnsafeMetadata } from '../../utils/resourceParams' ;
60+ import { executeProtectCheck } from '../../utils/protectCheck' ;
5961import { runAsyncResourceTask } from '../../utils/runAsyncResourceTask' ;
6062import { loadZxcvbn } from '../../utils/zxcvbn' ;
6163import {
@@ -480,6 +482,36 @@ export class SignUp extends BaseResource implements SignUpResource {
480482 } ) ;
481483 } ;
482484
485+ runProtectCheck = async ( ) : Promise < SignUpResource > => {
486+ // 1. Prepare — backend returns script URL in verifications.protect_check
487+ await this . _basePost ( { action : 'prepare_protect_check' } ) ;
488+
489+ const scriptUrl = this . verifications . protectCheck ?. url ;
490+ if ( ! scriptUrl ) {
491+ throw new ClerkRuntimeError ( 'No protect check script URL returned' , {
492+ code : 'protect_check_missing_url' ,
493+ } ) ;
494+ }
495+
496+ // 2. Get or create container
497+ const container = this . getOrCreateProtectCheckContainer ( ) ;
498+
499+ try {
500+ // 3. Load and execute script
501+ const result = await executeProtectCheck ( scriptUrl , this , container ) ;
502+
503+ // 4. Attempt with result
504+ await this . _basePost ( {
505+ body : result ,
506+ action : 'attempt_protect_check' ,
507+ } ) ;
508+ } finally {
509+ this . cleanupProtectCheckContainer ( container ) ;
510+ }
511+
512+ return this ;
513+ } ;
514+
483515 upsert = ( params : SignUpCreateParams | SignUpUpdateParams ) : Promise < SignUpResource > => {
484516 return this . id ? this . update ( params ) : this . create ( params ) ;
485517 } ;
@@ -583,6 +615,25 @@ export class SignUp extends BaseResource implements SignUpResource {
583615 return false ;
584616 }
585617
618+ private getOrCreateProtectCheckContainer ( ) : HTMLDivElement {
619+ let el = document . getElementById ( PROTECT_CHECK_CONTAINER_ID ) as HTMLDivElement | null ;
620+ if ( ! el ) {
621+ el = document . createElement ( 'div' ) ;
622+ el . id = PROTECT_CHECK_CONTAINER_ID ;
623+ document . body . appendChild ( el ) ;
624+ }
625+ return el ;
626+ }
627+
628+ private cleanupProtectCheckContainer ( el : HTMLDivElement ) {
629+ // Only remove from DOM if we created it (i.e., the UI didn't provide it)
630+ if ( el . parentNode && ! document . getElementById ( PROTECT_CHECK_CONTAINER_ID ) ) {
631+ el . remove ( ) ;
632+ }
633+ // Always clear inner content
634+ el . innerHTML = '' ;
635+ }
636+
586637 __experimental_getEnterpriseConnections = ( ) : Promise < SignUpEnterpriseConnectionResource [ ] > => {
587638 return BaseResource . _fetch ( {
588639 path : `/client/sign_ups/${ this . id } /enterprise_connections` ,
@@ -603,6 +654,7 @@ type SignUpFutureVerificationsMethods = Pick<
603654 | 'waitForEmailLinkVerification'
604655 | 'sendPhoneCode'
605656 | 'verifyPhoneCode'
657+ | 'runProtectCheck'
606658> ;
607659
608660class SignUpFutureVerifications implements SignUpFutureVerificationsType {
@@ -614,6 +666,7 @@ class SignUpFutureVerifications implements SignUpFutureVerificationsType {
614666 waitForEmailLinkVerification : SignUpFutureVerificationsType [ 'waitForEmailLinkVerification' ] ;
615667 sendPhoneCode : SignUpFutureVerificationsType [ 'sendPhoneCode' ] ;
616668 verifyPhoneCode : SignUpFutureVerificationsType [ 'verifyPhoneCode' ] ;
669+ runProtectCheck : SignUpFutureVerificationsType [ 'runProtectCheck' ] ;
617670
618671 constructor ( resource : SignUp , methods : SignUpFutureVerificationsMethods ) {
619672 this . #resource = resource ;
@@ -623,6 +676,7 @@ class SignUpFutureVerifications implements SignUpFutureVerificationsType {
623676 this . waitForEmailLinkVerification = methods . waitForEmailLinkVerification ;
624677 this . sendPhoneCode = methods . sendPhoneCode ;
625678 this . verifyPhoneCode = methods . verifyPhoneCode ;
679+ this . runProtectCheck = methods . runProtectCheck ;
626680 }
627681
628682 get emailAddress ( ) {
@@ -641,6 +695,10 @@ class SignUpFutureVerifications implements SignUpFutureVerificationsType {
641695 return this . #resource. verifications . externalAccount ;
642696 }
643697
698+ get protectCheck ( ) {
699+ return this . #resource. verifications . protectCheck ;
700+ }
701+
644702 get emailLinkVerification ( ) {
645703 if ( ! inBrowser ( ) ) {
646704 return null ;
@@ -681,6 +739,7 @@ class SignUpFuture implements SignUpFutureResource {
681739 waitForEmailLinkVerification : this . waitForEmailLinkVerification . bind ( this ) ,
682740 sendPhoneCode : this . sendPhoneCode . bind ( this ) ,
683741 verifyPhoneCode : this . verifyPhoneCode . bind ( this ) ,
742+ runProtectCheck : this . _runProtectCheck . bind ( this ) ,
684743 } ) ;
685744 }
686745
@@ -832,6 +891,46 @@ class SignUpFuture implements SignUpFutureResource {
832891 return { captchaToken, captchaWidgetType, captchaError } ;
833892 }
834893
894+ private async _runProtectCheck ( ) : Promise < { error : ClerkError | null } > {
895+ return runAsyncResourceTask ( this . #resource, async ( ) => {
896+ // 1. Prepare
897+ await this . #resource. __internal_basePost ( { action : 'prepare_protect_check' } ) ;
898+
899+ const scriptUrl = this . #resource. verifications . protectCheck ?. url ;
900+ if ( ! scriptUrl ) {
901+ throw new ClerkRuntimeError ( 'No protect check script URL returned' , {
902+ code : 'protect_check_missing_url' ,
903+ } ) ;
904+ }
905+
906+ // 2. Container
907+ let container = document . getElementById ( PROTECT_CHECK_CONTAINER_ID ) as HTMLDivElement | null ;
908+ const createdContainer = ! container ;
909+ if ( ! container ) {
910+ container = document . createElement ( 'div' ) ;
911+ container . id = PROTECT_CHECK_CONTAINER_ID ;
912+ document . body . appendChild ( container ) ;
913+ }
914+
915+ try {
916+ // 3. Execute script
917+ const result = await executeProtectCheck ( scriptUrl , this . #resource, container ) ;
918+
919+ // 4. Attempt
920+ await this . #resource. __internal_basePost ( {
921+ body : result ,
922+ action : 'attempt_protect_check' ,
923+ } ) ;
924+ } finally {
925+ if ( createdContainer ) {
926+ container . remove ( ) ;
927+ } else {
928+ container . innerHTML = '' ;
929+ }
930+ }
931+ } ) ;
932+ }
933+
835934 private async _create ( params : SignUpFutureCreateParams ) : Promise < void > {
836935 const { captchaToken, captchaWidgetType, captchaError } = await this . getCaptchaToken ( params ) ;
837936
0 commit comments