Skip to content

RecaptchaClear doesn't actually workΒ #8747

Open
@White-waluigi

Description

@White-waluigi

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 (
		<Bootstrap.Button {...props}  id={randomizedKey} key={randomizedKey} />
	);


}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions