I am using firebase auth and I wanted to add SMS MFA Verfication. I had struggled a lot integrating the required RecaptchaVerifier with my React Components. So I ended up trying to build a RecaptchaButton. Basically its job is to render a button and then perform a recaptch activated action and return the verifier. Here's what that looks like
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}) {
//Force the Button to rerender always, since Recaptcha attaches itself to the button and will stop working if a previous instance of itself has already attached itself to the button
const [randomizedKey,setRandomizedKey]=useState(randomAlphaNumeric(64));
useEffect(()=>{
//With this I am trying to avoid double renders. RecaptchaVerifier.clear doesn't actually work and the verifier will still be active, forcing us to use the verifier of the previous render (due to strict mode)
if(!window.recaptchaRepo || !window.recaptchaRepo[randomizedKey]){
const recaptchaVerifier = new RecaptchaVerifier(AuthSingleton.get().firebaseAuth, randomizedKey, {
"size": "invisible",
"callback": async function(response) {
//Recaptcha insist on being the only one allowed to handle the onClick event which is extremely annoying
onVerified(recaptchaVerifier);
},
// Response expired. Ask user to solve reCAPTCHA again.
onError(new Error("Recaptcha expired"));
}
,
"error-callback": function(e) {
onError(e);
}
});
//Despite this, Recaptcha Still finds a way to spit out errors
try{
const grepid=recaptchaVerifier.render().catch(()=>{});
}catch(e){
}
window.recaptchaRepo??={};
//This will cause a large buildup of unused RecaptchaVerfiers but theres no actual way to reset or release them.
window.recaptchaRepo[randomizedKey]=recaptchaVerifier;
}
},[]);
// Forcibly renrende the button
return (
<Bootstrap.Button {...props} id={randomizedKey} key={randomizedKey} />
);
}
This will still produce errors like this (and lots of em):
Uncaught TypeError: n is null
N recaptcha__en.js:514
F recaptcha__en.js:152
setTimeout handler*d< recaptcha__en.js:209
d recaptcha__en.js:152
T recaptcha__en.js:101
<anonymous> recaptcha__en.js:1123
E recaptcha__en.js:538
E recaptcha__en.js:538
d recaptcha__en.js:178
next recaptcha__en.js:401
p recaptcha__en.js:258
promise callback*I recaptcha__en.js:258
H recaptcha__en.js:259
A recaptcha__en.js:258
g recaptcha__en.js:534
g recaptcha__en.js:538
g recaptcha__en.js:547
WS recaptcha__en.js:749
L recaptcha__en.js:517
A recaptcha__en.js:280
l recaptcha__en.js:322
d recaptcha__en.js:145
l recaptcha__en.js:328
g recaptcha__en.js:547
ym recaptcha__en.js:985
R recaptcha__en.js:445
d recaptcha__en.js:207
WS recaptcha__en.js:749
L recaptcha__en.js:517
A recaptcha__en.js:280
l recaptcha__en.js:322
d recaptcha__en.js:145
l recaptcha__en.js:328
d recaptcha__en.js:206
O recaptcha__en.js:445
O recaptcha__en.js:411
g recaptcha__en.js:553
d recaptcha__en.js:147
MU recaptcha__en.js:594
N recaptcha__en.js:478
makeRenderPromise recaptcha_verifier.ts:246
render recaptcha_verifier.ts:172
ReCaptchaButton ReCaptchaButton.jsx:52
So far, this works for the most part. Unfortunately it is far from a clean solution. So is there a better way to integrate firebase auth RecaptchaVerifier into React? Or is there a way to bypass the firebase Recaptcha implmentation in favor of something like this?
I just want it to work without spitting out loads of errors