firebase / firebase/firebase-js-sdk

RecaptchaClear doesn't actually work

Open
#8,747 3 comments 1 reaction 0 assignees View on GitHub
api: auth needs-attention question stack:React
Dominant language
TypeScript
Stars
5.1k
Forks
1k
Avg merge
2d 21h
Merged PRs (30d)
37

Description

### Operating System

Manjaro

### Environment (if applicable)

node 23.3, React

### Firebase SDK Version

"firebase": "^11.1.0",

### Firebase SDK Product(s)

Auth

### Project Tooling

It is using react 18.3 with vite 6.0.01.

### Detailed Problem Description

I am trying to build ReCaptchaButton component in React JS. For this to work I need to be able to create a RecaptchaVerfier as soon as the elment is mounted and destroy it as soon as it is unmounted. Unfortunately, the destroy part doesn't work. (In Strict Mode, React mounts a component, then immedietly unmounts it and then mounts a new instance, to enforce re-usability)

This wouldn't be a problem, but unfortunately, recaptchaVerifier.clear doesn't seem to actually do anything. Meaning, it stays attached to the dom (which react keeps as is) even as the component itself unmounts. The next mount then encounters an already captcha infested button and then throws an Error.

The only way I've been able to sort of make this work, is by anticipating the remount using a window.alreadyMounted variable and then reuse the verfier of the already unmounted component. This is an ugly hack which itself spits out a bunch of errors, namely:
```

Uncaught TypeError: n is null
N recaptcha__en.js:514
F recaptcha__en.js:152
```

and that is despite my efforts to suppress thrown errors. From what I've found, nobody has yet been able to integrated firebase embedded RecaptchaVerfier into a clean React component, simply because .clear doesn't actually clear the component.

### Steps and code to reproduce issue

Here's the RecaptchaButton. Simply Mounting it once will spit out the Error:` reCAPTCHA has already been rendered in this element N recaptcha__en.js:477 Error`

```
import {
RecaptchaVerifier
} from "firebase/auth";
import {useState,useContext,useEffect} from 'react';
import * as Bootstrap from 'react-bootstrap';

import {AuthSingleton} from '../auth/AuthSingleton.js';
import * as auth from 'firebase/auth';

//Need these because recaptch is a mess
function randomAlphaNumeric(length = 64) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const randomValues = new Uint8Array(length);
crypto.getRandomValues(randomValues);

for (let i = 0; i < length; i++) {
result += chars[randomValues[i] % chars.length];
}

return result;
}

export function ReCaptchaButton({onVerified,onError,...props}) {

const [randomizedKey,setRandomizedKey]=useState(randomAlphaNumeric(64));

useEffect(()=>{

const recaptchaVerifier = new RecaptchaVerifier(AuthSingleton.get().firebaseAuth, randomizedKey, {
"size": "invisible",
"callback": async function(response) {
// reCAPTCHA solved, you can proceed with
// phoneAuthProvider.verifyPhoneNumber(...).
onVerified(recaptchaVerifier);
},
"expired-callback": function() {
// Response expired. Ask user to solve reCAPTCHA again.
onError(new Error("Recaptcha expired"));
}
,
"error-callback": function(e) {
onError(e);
}

});
try{
recaptchaVerifier.render().catch(()=>{});
}catch(e){
}
// This will be executed as soon as the component unmounts
return ()=>{
// This doesn't work
recaptchaVerifier.clear();
};

},[]);

return (

);

}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.