Reputation: 618
I'm trying to load a RecaptchaVerifier into my React class, so I can enable Phone Auth for my web app. However, when I submit my phone number, I get the following error:
"signInWithPhoneNumber failed: Second argument "applicationVerifier" must be an implementation of firebase.auth.ApplicationVerifier."
The entire console log for this error
I logged the window.recaptchaVerifier, to find it is silently erroring with the error code:
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:2:14)
The Recaptcha doesn't even render, and the window.repcaptchaVerfier.render() method spits another error (seems like that part was optional in the docs, so I removed it).
constructor(props) {
super(props);
this.state = {
sendOTP: false,
phone_number: "",
currentUser: null,
otp: "",
isButtonDisabled: true,
error: '',
cardAnimation: 'cardHidden',
};
}
componentDidMount() {
this.setupRecaptcha();
setTimeout(() => {
this.setState({cardAnimation: ""});
}, 700);
// window.recaptchaVerifier.render().then(widgetId => {
// window.recaptchaWidgetId = widgetId;
// });
// Removed, since it errors on load with this code.
}
setupRecaptcha = () => {
window.recaptchaVerifier = new this.props.firebase.recaptchaVerifier(
'recaptcha-container',
{
size: "normal",
callback: response => {
// reCAPTCHA solved, allow signInWithPhoneNumber.
// ...
this.setState({ isButtonDisabled: false });
},
"expired-callback": response => {
// Response expired. Ask user to solve reCAPTCHA again.
// ...
this.setState({ isButtonDisabled: true, error: "Recaptcha Expired. Please try again." });
}
}
);
console.log(window.recaptchaVerifier)
// Here is where i get the silent error about "strict" mode.
};
handleLogin = () => {
let appVerifier = window.recaptchaVerifier;
this.props.firebase.doSignInWithPhoneNumber(this.state.phone_number, appVerifier).then(confirmationResult => {
this.setState({sendOTP: true});
window.confirmationResult = confirmationResult;
}).catch(err => {
this.setState({error: err})
})
};
handleOTPCheck = () => {
window.confirmationResult
.confirm(this.state.otp)
.then(function(result) {
// User signed in successfully.
console.log(result);
// ...
})
.catch(function(error) {
// User couldn't sign in (bad verification code?)
// ...
});
};
handleSubmit(event) {
event.preventDefault();
}
onChange = event => {
this.setState({ [event.target.name]: event.target.value });
};
render() {
const { phone_number, otp, error } = this.state;
const isInvalid = phone_number === '';
const { classes } = this.props;
return (
<div
className={classes.pageHeader}
style={{
backgroundImage: "url(" + backgroundImage + ")",
backgroundSize: "cover",
backgroundPosition: "top center"
}}
>
<div className={classes.container}>
<Grid container justify="center">
<Grid item xs={12} sm={12} md={4}>
<Card className={classes[this.state.cardAnimation]}>
<form className={classes.form}>
<CardHeader color="primary" className={classes.cardHeader}>
<Typography variant={'h4'}>Login</Typography>
</CardHeader>
<CardBody>
<TextField
label={"Phone Number"}
id="phone_number"
name={"phone_number"}
fullWidth
InputProps={{
endAdornment: <InputAdornment position="end"><Phone/></InputAdornment>,
}}
type={"tel"}
/>
<TextField
label="One-Time Password"
id="otp"
name={"otp"}
fullWidth
InputProps={{
endAdornment: <InputAdornment position="end"><Lock/></InputAdornment>,
}}
type={'password'}
autoComplete={'false'}
/>
</CardBody>
<div id={'recaptcha-container'}/>
<p className={classes.divider}>Standard messaging rates may apply.</p>
<CardFooter className={classes.cardFooter}>
<Button color="primary" size="large" onClick={this.handleLogin}>
Verify
</Button>
</CardFooter>
</form>
</Card>
</Grid>
</Grid>
</div>
<Footer whiteFont />
</div>
);
}
}
const SignInForm = compose(
withRouter,
withFirebase,
)(SignInFormBase);
/// this is then rendered in a functional react component in this page
I have a higher-order component that gives me firebase context. The function called at this.props.firebase.recaptchaVerifier
within my setupRecaptcha function is:
class Firebase() {
constructor() {
// initialise firebase etc
this.authRef = app().auth;
this.auth.useDeviceLanguage();
}
recaptchaVerifier = (container, parameters) =>
this.authRef.RecaptchaVerifier(container, parameters);
}
I'm well and truly stumped!
EDIT:
I managed to fix it. I moved the 'new' statement into my context provider:
recaptchaVerifier = (container, params) => {
return new this.authRef.RecaptchaVerifier(container, params);
};
setupRecaptcha = () => {
window.recaptchaVerifier = this.props.firebase.recaptchaVerifier('recaptcha-container', {
'size': 'normal',
'callback': response => {
// reCAPTCHA solved, allow signInWithPhoneNumber.
this.setState({isButtonDisabled: false});
},
'expired-callback': response => {
// Response expired. Ask user to solve reCAPTCHA again.
this.setState({isButtonDisabled: true, error: "Recaptcha Expired. Please try again."});
}
});
window.recaptchaVerifier.render().then(widgetId => {
window.recaptchaWidgetId = widgetId;
});
};
Upvotes: 5
Views: 7447
Reputation: 618
I managed to fix it. I moved the 'new' statement into my context provider:
// In context.js
recaptchaVerifier = (container, params) => {
return new this.authRef.RecaptchaVerifier(container, params);
};
// in my component
setupRecaptcha = () => {
window.recaptchaVerifier = this.props.firebase.recaptchaVerifier('recaptcha-container', {
'size': 'normal',
'callback': response => {
// reCAPTCHA solved, allow signInWithPhoneNumber.
this.setState({isButtonDisabled: false});
},
'expired-callback': response => {
// Response expired. Ask user to solve reCAPTCHA again.
this.setState({isButtonDisabled: true, error: "Recaptcha Expired. Please try again."});
}
});
window.recaptchaVerifier.render().then(widgetId => {
window.recaptchaWidgetId = widgetId;
});
};```
Upvotes: 2