firebase-js-sdk icon indicating copy to clipboard operation
firebase-js-sdk copied to clipboard

RecaptchaVerifier error when used together with AppCheck

Open faizanabidnaqvi opened this issue 3 years ago • 16 comments

[REQUIRED] Describe your environment

  • Operating System version: MacOS Monterey 12.3
  • Browser version: Chrome version 100.0.4896.75
  • Firebase SDK version: 9.6.10
  • Firebase Product: auth

[REQUIRED] Describe the problem

When I use the RecaptchaVerifier on my site with AppCheck enabled, I get an error when I call the verify method on the RecaptchaVerifier. The error is as follows: TypeError: this.getAssertedRecaptcha(...).render is not a function This only happens when AppCheck is enabled; without AppCheck it behaves normally, that is, the verification goes through. Also, it doesn't happen in development or production when running at localhost with the emulator. Only occurs in the deployed code in firebase hosting.

Screenshot 2022-04-10 at 6 29 34 PM

Steps to reproduce:

  1. Create a react app with firebase app check enabled
  2. Create a simple page with an invisible RecaptchaVerifier and a button that when clicked calls 'RecaptchaVerifier.verify()'
  3. Build the app and deploy to hosting
  4. Notice the error occurs when you press the button that calls the verify function

Relevant Code:

In my root App.tsx file, appcheck is initialized this way

if(!getApps().length){
  const app = initializeApp(firebaseConfig)
  // console.log(`Initializing app check with ${import.meta.env.VITE_RECAPTCHA_KEY}`)
  //if localhost set debug app check token
  //https://firebase.google.com/docs/app-check/web/debug-provider?authuser=0&hl=en
  if (window.location.hostname === "localhost"){
    //this must be set to true to generate a new token and once generated and registered, we need to set the token value in env
    //(self as any).FIREBASE_APPCHECK_DEBUG_TOKEN = true
    (window.self as any).FIREBASE_APPCHECK_DEBUG_TOKEN = import.meta.env.VITE_APP_CHECK_DEBUG_TOKEN
  }

  initializeAppCheck(app, {
    provider: new ReCaptchaEnterpriseProvider(import.meta.env.VITE_RECAPTCHA_KEY),
    isTokenAutoRefreshEnabled: true
  })
}

The RecaptchaVerifier component is as follows

export const FirebaseRecaptcha = ({instructionMessage, onVerificationComplete}: FirebaseRecaptchaProps)=>{

    const [isCaptchaLoading, setCaptchaLoading] = useState(false)
    const [verificationErrored, setVerificationErrored]= useState(false)
  
    let verifier: any = null
    useEffect(()=>{
      console.log("Initializing captcha");
  
      verifier = new RecaptchaVerifier("recaptcha-container", {
        'size': 'invisible',
        'callback': function(response: any) {
          console.log("verification success")
          setCaptchaLoading(false)
          onVerificationComplete(CaptchaVerificationStatus.SUCCESS)
        }
      }, getAuth())
    },[])
  
    const onStartClick = async ()=> {
      if(verifier){
        try {
          setCaptchaLoading(true)
          console.log("About to verify")
          const res = await verifier.verify()
        }catch(e){
          setCaptchaLoading(false)
          console.error(e)
          onVerificationComplete(CaptchaVerificationStatus.FAIL)
        }
      }
    }
  
    // <Button variant="contained" color="primary" id={recaptchaButtonId} onClick={()=>onStartClick()} className={`${classes.actionButton} ${classes.bottomMargin}`}>
    //   Start
    // </Button>

    return (
        <>
          <div id="recaptcha-container"></div>
          <Typography variant="h6" gutterBottom sx={{mb: 2}}>
            {instructionMessage ? instructionMessage : getStrings().message_start_page}
          </Typography>
          {isCaptchaLoading ?
            <CircularProgress sx={{mb: 2}} /> :
            <Button variant="contained" color="primary" onClick={()=>onStartClick()} sx={{mb: 2, width: '50%'}}>
              {getStrings().button_start}
            </Button>
          }
        </>
      )
  }

faizanabidnaqvi avatar Apr 10 '22 17:04 faizanabidnaqvi

Hi @faizanabidnaqvi, thanks for the report. I was able to reproduce the behavior now. Let me check what we can do for this issue or bring someone here that can provide more context about it. I’ll update this thread if I have any information to share.

jbalidiong avatar Apr 14 '22 08:04 jbalidiong

I am currently experiencing this. Phone signin worked before app check. After app check, getting error "this.getAssertedRecaptcha(...).render is not a function".

I unenforced app check for now since I need my website to require my users to register both email and phone. Hoping for a prompt resolution on this.

votenex avatar May 17 '22 05:05 votenex

Is there an update on this?

votenex avatar May 26 '22 04:05 votenex

any update on this issue?

VFertak avatar Jun 15 '22 05:06 VFertak

any update?

stjoe4 avatar Jun 21 '22 05:06 stjoe4

It works with ReCaptchaV3Provider

stjoe4 avatar Jun 21 '22 11:06 stjoe4

It works with ReCaptchaV3Provider

I used app check ReCaptchaV3Provider with phone signin. I had the same issue. So it worked for you?

votenex avatar Jun 22 '22 02:06 votenex

Yes it works with ReCaptchaV3Provider, but have the same error with ReCaptchaEnterpriseProvider.

stjoe4 avatar Jun 23 '22 20:06 stjoe4

Thank you for reporting this issue. We're looking into it. Internal bug filed: b/237289338.

weixifan avatar Jun 27 '22 17:06 weixifan

I actually haven't been able to reproduce this by using the code in the first post and trying to fill in the blanks. I've tried localhost dev server, localhost prod build, and deployed Firebase hosting, and they all work. Does anyone have a minimal repro I can use? (preferably one that errors in localhost, but if not, just let me know what environment)

hsubox76 avatar Jul 06 '22 21:07 hsubox76

When I had this error, I tried both v3 and enterprise. It doesn't even get to the verify code part.

app check:

const appCheck = initializeAppCheck(firebaseApp, {
    provider: new ReCaptchaV3Provider('***********************'),
    isTokenAutoRefreshEnabled: true
});

phone auth:

setPersistence(auth, browserLocalPersistence)
        .then(() => {
            // Sign in
            signInWithPhoneNumber(auth, usermobile, recaptchaVerifier)
            .then((confirmationResult) => {
                $('#otpcode').show();
                $('#phone-signin-sendcode').hide();
                $('#phone-signin-verifycode').show();

                $('#phone-signin-verifycode').click(async function() {
                    const code = document.getElementById('otpcode').value;
                    window.confirmationResult = confirmationResult;

                    let x = {
                        mobile: usermobile,
                        userid: z,
                        code: code
                    }
                    verifyCode(x)
                })

            }).catch((error) => {
                alert('An error occurred! ' + error.code + " " + error.message);
                window.recaptchaVerifier.render().then(function(widgetId) {
                    grecaptcha.reset(widgetId);
                });
            });
        })
        .catch((error) => {
            alert('An error occurred! ' + error.code + " " + error.message);
        });

even if I used self.FIREBASE_APPCHECK_DEBUG_TOKEN = true; It still didn't work.

a few weeks ago, v3 started working for me. But enterprise still doesn't work.

votenex avatar Jul 07 '22 05:07 votenex

I'm able to reproduce with App Check's ReCaptchaEnterpriseProvider only, and I think I can get a quick fix done for that. If you're experiencing this error with ReCaptchaV3Provider, it would be great if you can provide a reliable minimal repro of it with all the extraneous parts stripped out. Will post a PR for the Enterprise fix soon.

hsubox76 avatar Jul 07 '22 18:07 hsubox76

Let me keep this open because there's still some uncertainty about if there is a bug with RecaptchaV3.

hsubox76 avatar Jul 07 '22 21:07 hsubox76

I have this error and it started when I switched from using regular recaptcha to enterprise recaptcha on my firebase project. Seems like there's a difference between the object that gets returned by enterprise vs normal recaptcha, this is the object with the missing method in debug mode Screen Shot 2022-08-01 at 2 39 12 PM

it seems like it may be a result of both libraries (enterprise and non-, which is i assume what the auth library uses) loading on the same page

ehedaya avatar Aug 01 '22 18:08 ehedaya

This should be fixed in 9.9.1, if it's not the case, can you try a fresh reinstall to make sure nothing was left behind? (remove node_modules and yarn.lock and reinstall). If that doesn't work, can you provide a minimal repro? Similar comment from https://github.com/firebase/firebase-js-sdk/issues/6485#issuecomment-1201612743:

I'm not able to reproduce this, are you sure you are using the latest version? It should have been fixed by https://github.com/firebase/firebase-js-sdk/issues/6133. Is it possible something got left over when upgrading from an older version? Can you clean out your node_modules and yarn.lock and try to reinstall firebase? I tried to create a minimal reproduction using this code:

const appCheck = initializeAppCheck(app, { provider: new ReCaptchaEnterpriseProvider('MY_SITE_KEY')});

const verifier = new RecaptchaVerifier('container', { size: 'invisible' }, getAuth(app));

const provider = new PhoneAuthProvider(getAuth(app));
await provider.verifyPhoneNumber('+1-510-555-5555', verifier);

And everything seemed to work. If a fresh install doesn't help, how and when are you initializing App Check? I didn't see it in the sample code.

It sounds like there may still be an issue but I'm unable to reproduce it on my end. I've tried swapping the order of App Check and RecaptchaVerifier and it doesn't seem to break it either.

hsubox76 avatar Aug 01 '22 19:08 hsubox76

Thanks! That did fix it!

ehedaya avatar Aug 04 '22 14:08 ehedaya

I haven't heard back from the other person who reported it in https://github.com/firebase/firebase-js-sdk/issues/6485 so I'll close this again, can reopen if someone reports the problem again.

hsubox76 avatar Aug 15 '22 17:08 hsubox76