How to Avoid Showing reCAPTCHA WebView During Authentication in React Native App Using Firebase #129370
Unanswered
M7mad94
asked this question in
Programming Help
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Body
I'm developing a React Native app that uses Firebase for phone authentication. During the signup or login process, a reCAPTCHA WebView appears, asking users to verify they are not robots. This happens both in the simulator and on real devices.
Here is a screenshot of the WebView that appears:
For a better user experience, I want to avoid showing this WebView. I've read that it is possible to use an invisible reCAPTCHA verifier, but I'm not sure how to implement this in my React Native app.
import React, { useState, useEffect, useRef } from 'react'
import {
Image,
Keyboard,
TextInput,
Text,
TouchableOpacity,
View,
Animated,
Easing,
ImageBackground
} from 'react-native'
import PhoneInput from 'react-native-phone-input'
import {
CodeField,
Cursor,
useBlurOnFulfill,
useClearByFocusCell,
} from 'react-native-confirmation-code-field'
import { useNavigation, useRoute } from '@react-navigation/core'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
import appleAuth, {
AppleButton,
} from '@invertase/react-native-apple-authentication'
import {
useTheme,
useTranslations,
ActivityIndicator,
Alert,
ProfilePictureSelector,
CountriesModalPicker,
} from '../../../dopebase'
import { setUserData } from '../../redux/auth'
import { useDispatch } from 'react-redux'
import { localizedErrorMessage } from '../../api/ErrorCode'
import TermsOfUseView from '../../components/TermsOfUseView'
import dynamicStyles from './styles'
import IMGoogleSignInButton from '../../components/IMGoogleSignInButton/IMGoogleSignInButton'
import { useOnboardingConfig } from '../../hooks/useOnboardingConfig'
import { useAuth } from '../../hooks/useAuth'
const codeInputCellCount = 6
const SmsAuthenticationScreen = () => {
const navigation = useNavigation()
const route = useRoute()
const {
isSigningUp,
isConfirmSignUpCode,
isConfirmResetPasswordCode,
email,
userInfo,
} = route.params
const { localized } = useTranslations()
const { theme, appearance } = useTheme()
const authManager = useAuth()
const dispatch = useDispatch()
const styles = dynamicStyles(theme, appearance)
const { config } = useOnboardingConfig()
const [inputFields, setInputFields] = useState({})
const [loading, setLoading] = useState(false)
const [isPhoneVisible, setIsPhoneVisible] = useState(
!isConfirmSignUpCode && !isConfirmResetPasswordCode,
)
const [phoneNumber, setPhoneNumber] = useState(false)
const [countriesPickerData, setCountriesPickerData] = useState(null)
const [verificationId, setVerificationId] = useState(null)
const [profilePictureFile, setProfilePictureFile] = useState(null)
const [countryModalVisible, setCountryModalVisible] = useState(false)
const [codeInputValue, setCodeInputValue] = useState('')
const scaleValue = useRef(new Animated.Value(1)).current;
const myCodeInput = useBlurOnFulfill({
codeInputValue,
value: codeInputValue,
cellCount: codeInputCellCount,
})
const [codeInputProps, getCellOnLayoutHandler] = useClearByFocusCell({
codeInputValue,
value: codeInputValue,
setCodeInputValue,
setValue: setCodeInputValue,
})
const phoneRef = useRef(null)
useEffect(() => {
animateBackground();
}, [])
useEffect(() => {
if (codeInputValue?.trim()?.length === codeInputCellCount) {
onFinishCheckingCode(codeInputValue)
}
}, [codeInputValue])
useEffect(() => {
if (phoneRef && phoneRef.current) {
setCountriesPickerData(phoneRef.current.getPickerData())
}
}, [phoneRef])
const animateBackground = () => {
Animated.loop(
Animated.sequence([
Animated.timing(scaleValue, {
toValue: 1.3,
duration: 10000,
easing: Easing.inOut(Easing.ease),
useNativeDriver: true,
}),
Animated.timing(scaleValue, {
toValue: 1,
duration: 5000,
easing: Easing.inOut(Easing.ease),
useNativeDriver: true,
}),
]),
).start();
};
const onFBButtonPress = () => {
setLoading(true)
authManager.loginOrSignUpWithFacebook(config).then(response => {
if (response?.user) {
const user = response.user
dispatch(setUserData({ user }))
Keyboard.dismiss()
navigation.reset({
index: 0,
routes: [{ name: 'MainStack', params: { user } }],
})
} else {
setLoading(false)
Alert.alert(
'',
localizedErrorMessage(response.error, localized),
[{ text: localized('OK') }],
{
cancelable: false,
},
)
}
})
}
const onGoogleButtonPress = () => {
setLoading(true)
authManager.loginOrSignUpWithGoogle(config).then(response => {
if (response?.user) {
const user = response.user
dispatch(setUserData({ user }))
Keyboard.dismiss()
navigation.reset({
index: 0,
routes: [{ name: 'MainStack', params: { user } }],
})
} else {
setLoading(false)
Alert.alert(
'',
localizedErrorMessage(response.error, localized),
[{ text: localized('OK') }],
{
cancelable: false,
},
)
}
})
}
const onAppleButtonPress = async () => {
setLoading(true)
authManager.loginOrSignUpWithApple(config).then(response => {
if (response?.user) {
const user = response.user
dispatch(setUserData({ user }))
Keyboard.dismiss()
navigation.reset({
index: 0,
routes: [{ name: 'MainStack', params: { user } }],
})
} else {
setLoading(false)
Alert.alert(
'',
localizedErrorMessage(response.error, localized),
[{ text: localized('OK') }],
{
cancelable: false,
},
)
}
})
}
const signInWithPhoneNumber = userValidPhoneNumber => {
setLoading(true)
authManager.sendSMSToPhoneNumber(userValidPhoneNumber).then(response => {
setLoading(false)
const confirmationResult = response.confirmationResult
if (confirmationResult) {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
window.confirmationResult = confirmationResult
setVerificationId(confirmationResult.verificationId)
setIsPhoneVisible(false)
} else {
// Error; SMS not sent
Alert.alert(
'',
localizedErrorMessage(response.error, localized),
[{ text: localized('OK') }],
{ cancelable: false },
)
}
})
}
const trimFields = fields => {
var trimmedFields = {}
Object.keys(fields).forEach(key => {
if (fields[key]) {
trimmedFields[key] = fields[key].trim()
}
})
return trimmedFields
}
const signUpWithPhoneNumber = smsCode => {
const userDetails = {
...trimFields(inputFields),
phone: phoneNumber?.trim(),
photoFile: profilePictureFile,
}
authManager
.registerWithPhoneNumber(
userDetails,
smsCode,
verificationId,
config.appIdentifier,
)
.then(response => {
setLoading(false)
if (response.error) {
Alert.alert(
'',
localizedErrorMessage(response.error, localized),
[{ text: localized('OK') }],
{ cancelable: false },
)
} else {
const user = response.user
dispatch(setUserData({ user }))
Keyboard.dismiss()
navigation.reset({
index: 0,
routes: [{ name: 'MainStack', params: { user } }],
})
}
})
}
const onPressSend = async () => {
if (phoneRef.current.isValidNumber()) {
const userValidPhoneNumber = phoneRef.current.getValue()
setLoading(true)
setPhoneNumber(userValidPhoneNumber)
if (isSigningUp) {
const { error } = await authManager.validateUsernameFieldIfNeeded(
trimFields(inputFields),
config,
)
}
const onPressFlag = () => {
setCountryModalVisible(true)
}
const onPressCancelContryModalPicker = () => {
setCountryModalVisible(false)
}
const onFinishCheckingCode = newCode => {
setLoading(true)
if (isSigningUp) {
signUpWithPhoneNumber(newCode)
return
}
}
const onChangeInputFields = (text, key) => {
setInputFields(prevFields => ({
...prevFields,
[key]: text,
}))
}
const selectCountry = country => {
phoneRef.current.selectCountry(country.iso2)
}
const renderPhoneInput = () => {
}
const renderCodeInputCell = ({ index, symbol, isFocused }) => {
let textChild = symbol
}
const renderCodeInput = () => {
return (
<CodeField
ref={myCodeInput}
{...codeInputProps}
value={codeInputValue}
onChangeText={setCodeInputValue}
cellCount={codeInputCellCount}
keyboardType="number-pad"
textContentType="oneTimeCode"
renderCell={renderCodeInputCell}
/>
)
}
const renderInputField = (field, index) => {
return (
<TextInput
key={index?.toString()}
style={styles.InputContainer}
placeholder={field.placeholder}
placeholderTextColor="#aaaaaa"
onChangeText={text => onChangeInputFields(text, field.key)}
value={inputFields[field.key]}
underlineColorAndroid="transparent"
/>
)
}
const renderAsSignUpState = () => {
return (
<>
{localized('Create new account')}
{!isConfirmSignUpCode && (
)}
{!isConfirmSignUpCode && config.smsSignupFields.map(renderInputField)}
{isPhoneVisible ? renderPhoneInput() : renderCodeInput()}
{isConfirmSignUpCode && (
{localized('Please check your e-mail for a confirmation code.')}
)}
{!isConfirmSignUpCode && (
<>
{localized('OR')}
<TouchableOpacity
style={styles.signWithEmailContainer}
onPress={() => navigation.navigate('Signup')}>
{localized('Sign up with E-mail')}
</>
)}
</>
)
}
const renderAsLoginState = () => {
const appleButtonStyle = config.isAppleAuthEnabled
? {
dark: AppleButton?.Style?.WHITE,
light: AppleButton?.Style?.BLACK,
'no-preference': AppleButton?.Style?.WHITE,
}
: {}
}
return (
<View style={{ flex: 1,}}>
<Animated.View style={[styles.backgroundContainer, { transform: [{ scale: scaleValue }] }]}>
<ImageBackground style={styles.backgroundImage} source={require('../../../../assets/images/Lusail-Bg.png')}>
</Animated.View>
<KeyboardAwareScrollView
style={{ flex: 1, width: '100%' }}
keyboardShouldPersistTaps="always">
<TouchableOpacity onPress={() => navigation.goBack()}>
<View style={{
backgroundColor: "#fff",
maxHeight: "100%",
maxWidth: "96%",
borderRadius: 18,
marginHorizontal: "2%",
marginVertical: "30%",
paddingBottom: 20
}}>
)
}
export default SmsAuthenticationScreen
Guidelines
Beta Was this translation helpful? Give feedback.
All reactions