Firebase with google auth automatically select user. So i have no access to the "choose account" page.
I can't use :
GoogleSignin.revokeAccess()
because GoogleSignin is now deprecated
And :
export const provider = new GoogleAuthProvider();
provider.setCustomParameters({
prompt: "select_account",
});
only works on browers.
the SignIn and SignOut method works as they should, here is the code if needed:
const GoogleLogin = () => {
const [request, response, promptAsync] = useIdTokenAuthRequest({
clientId: process.env.REACT_APP_CLIENT_ID_WEB,
});
useEffect(() => {
if (response?.type === "success") {
const { id_token } = response.params;
const auth = getAuth();
const credential = GoogleAuthProvider.credential(id_token);
signInWithCredential(auth, credential);
}
}, [response]);
return (
<TouchableOpacity
onPress={() => {
promptAsync();
}}
style={styles.button3}
disabled={!request}
>
<Text style={styles.buttonText}>Login with Google</Text>
</TouchableOpacity>
);
};
Is there a way so i can ket the user choose an account they prefer to use?
Thanks for the helps.
Related
i am trying to add a signin buttun that work with firebase to store the sign in data i am using this pakage #react-native-google-signin/google-signin but i am getting this error can any one help me?
what i have tryed is here
export default function Sign({navigation, log}) {
const [userInfos, setuserInfo] = useState();
useEffect(() => {
GoogleSignin.configure({
webClientId:
'1082705867948-lgmqb8qpfc04jorik36idpbdvsbb78ud.apps.googleusercontent.com',
});
}, []);
async function onGoogleButtonPress() {
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
// console.log(userInfo);
setuserInfo(userInfo);
// Check if your device supports Google Play
// await GoogleSignin.hasPlayServices({showPlayServicesUpdateDialog: true});
// Get the users ID token
// const {idToken} = await GoogleSignin.signIn();
// Create a Google credential with the token
// const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// navigation.navigate('Signupfor', {userInfo});
// Sign-in the user with the credential
// return auth().signInWithCredential(googleCredential);
navigation.navigate('Signupfor', {userInfo, log});
}
return (
<View style={styles.prheight}>
<View style={styles.buttonw}>
<GoogleSigninButton
style={{width: 192, height: 48}}
size={GoogleSigninButton.Size.Wide}
color={GoogleSigninButton.Color.Light}
onPress={onGoogleButtonPress}
// disabled={this.state.isSigninInProgress}
/>
</View>
</View>
);
When I'm going to authenticate with phone number I get this error
how can I use reCaptcha I can't use safe net Method in phone authentication because my country is not exist when I want to create google cloud account.
this is my code.
please help me and if you have a good doc for solving this problem send me its link
import firebase from '#react-native-firebase/app';
import auth from '#react-native-firebase/auth';
const firebaseConfig = {
apiKey: 'xxxxxxxxxxxx',
authDomain: 'xxxxxxxxxxxxx',
projectId: 'xxxxxxxxxxxxx',
storageBucket: 'xxxxxxxxxxxxxx',
messagingSenderId: 'xxxxxxxxxxxxx',
appId: 'xxxxxxxxxxxxxxx',
measurementId: 'xxxxxxxxxxxxxx',
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
export default () => {
return { firebase, auth };
};
const SignUPScreen = ({ navigation }) => {
const { auth } = firebaseSetup();
const [confirm, setConfirm] = useState(null);
const [phoneNumber, setPhoneNumber] = useState('');
const signIn = async () => {
try {
const confirmation = await auth().signInWithPhoneNumber(phoneNumber);
setConfirm(confirmation);
navigation.navigate('OTP', { confirm: confirm });
} catch (error) {
alert(error);
}
};
return (
<View style={styles.container}>
<TextInput
style={styles.textInput}
placeholder="Enter your phone number"
keyboardType="phone-pad"
onChangeText={(text) => setPhoneNumber(text)}
/>
<Button title="Sign Up" onPress={() => signIn()} />
</View>
);
};
import { useState } from 'react';
import { Alert, Button, StyleSheet, View } from 'react-native';
import { TextInput } from 'react-native-paper';
const OTP = ({ route }) => {
const { confirm } = route.params;
const [code, setCode] = useState('');
const confirmCode = async () => {
try {
await confirm.confirm(code);
Alert.alert('User sign in SuccessFully');
} catch (error) {
Alert.alert('Invalid code');
}
};
return (
<View style={styles.container}>
<TextInput
style={styles.textInput}
placeholder="Enter the OTP code"
value={code}
onChangeText={(text) => setCode(text)}
/>
<Button title="Confirm Code" onPress={() => confirmCode()} />
</View>
);
};
I found that the problem is due to SHA1.
I had filled in an wrong SHA1 key in the Firebase console.
I change it with correct SHA1 then the error have been gone.
Notice: I have seen this question, but creating a whole landing page just to verify a user seems a bit much.
I added a login functionality to my react-native app using firebase/auth with email and password. This works well so far and I have no issues doing that.
I then continued to send a verification email to a new user and only allow him/her to use the app, once the email is verified. Again, no issues here.
The next step would be to login the user right after the email was verified. This is where I'm stuck, since the onAuthStateChanged eventhandler doesn't update after the user pressed the verification link in the email.
Is there any way to listen to the emailVerified state in real-time? I tried to use polling with setInterval() but this is not great since there is a notable delay between verification and login. I read about a continueLink you can pass to sendEmailVerification, but I couldn't figure out how to make that work in react-native.
I'm using Expo and therefore the Firebase SDK, not the Firebase react native package.
Here is the code I use for the signup:
export const signUp = async (username: string, email: string, password: string) => {
try {
const auth = getAuth();
if (email && password && username) {
// sign up
const userCredential = await createUserWithEmailAndPassword(auth, email, password);
// save username in firestore
await setUserName(userCredential, username);
// send Email Verification
await sendEmailVerification(userCredential.user);
return true;
}
} catch (error) {
onError(error);
}
};
And this is my onAuthStateChanged handler:
auth.onAuthStateChanged(authenticatedUser => {
try {
if (authenticatedUser?.emailVerified) {
setUser(authenticatedUser)
} else {
setUser(null)
}
} catch (error) {
console.log(error);
}
});
So in the end I did follow this question, but I changed it a bit to fit my needs. I'll post my steps for anyone who's doing the same.
Create a simple static website with firebase init and host it on firebase or somewhere else (check the hosting tab in your firebase console to get started)
Follow this guide to create the appropriate handlers on the website
Add the following to your verificationHandler to update the user (don't forget to import firestore) (I send the userId via the continueURL, but there are probably better ways)
// You can also use realtime database if you want
firebase.firestore().collection("users").doc(userId).set({
emailVerified: true
}, {merge: true}).then(() => {
message.textContent = "Your email has been verified.";
}).catch((error) => {
message.textContent = "The verification was invalid or is expired. Please try to send another verification email from within the app.";
});
Got to authentication -> templates in your firebase console and change the action url to your hosted website's url
Add a listener to the firestore doc to your react-native app
const onUserDataChanged = (uid, callback) => {
onSnapshot(doc(firestore, "users", uid), doc => callback(doc.data()));
}
Use the data from the callback to update the login state in the app
// As an example
auth.onAuthStateChanged(authenticatedUser => {
if (authenticatedUser && !authenticatedUser.emailVerified) {
unsubscribeFirestoreListener?.();
unsubscribeFirestoreListener = onUserDataChanged(authenticatedUser.uid, (data: any) => {
if (data?.emailVerified) {
setUser(authenticatedUser);
unsubscribeFirestoreListener?.();
}
});
}
}
use the codes below for your authentication context. for user id, you should use 'user.uid'
import React, { useState, createContext } from "react";
import * as firebase from "firebase";
import { loginRequest } from "./authentication.service";
export const AuthenticationContext = createContext();
export const AuthenticationContextProvider = ({ children }) => {
const [isLoading, setIsLoading] = useState(false);
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
firebase.auth().onAuthStateChanged((usr) => {
if (usr) {
setUser(usr);
setIsLoading(false);
} else {
setIsLoading(false);
}
});
const onLogin = (email, password) => {
setIsLoading(true);
firebase.auth().signInWithEmailAndPassword(email, password)
.then((u) => {
setUser(u);
setIsLoading(false);
})
.catch((e) => {
setIsLoading(false);
setError(e.toString());
});
};
const onRegister = (email, password, repeatedPassword) => {
setIsLoading(true);
if (password !== repeatedPassword) {
setError("Error: Passwords do not match");
return;
}
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((u) => {
setUser(u);
setIsLoading(false);
})
.catch((e) => {
setIsLoading(false);
setError(e.toString());
});
};
const onLogout = () => {
setUser(null);
firebase.auth().signOut();
};
return (
<AuthenticationContext.Provider
value={{
isAuthenticated: !!user,
user,
isLoading,
error,
onLogin,
onRegister,
onLogout,
}}
>
{children}
</AuthenticationContext.Provider>
);
};
I've implemented some basic registration/login functionality using Firebase email/password authentication. Everything works just fine on a simulator and via the Expo Go app however when I use expo build:android and install the apk on my android phone it seems to crash on onAuthStateChanged with no errors. I've tried to catch the error but getting no luck.
I've set it up as follows:
RootNavigator.js:
export default function RootNavigator({ navigation }) {
const [errorMsg, setErrorMsg] = useState();
const { user, setUser } = useContext(AuthenticatedUserContext);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const unsubscribeAuth = auth.onAuthStateChanged(async authenticatedUser => {
try {
await (authenticatedUser ? setUser(authenticatedUser) : setUser(null));
setIsLoading(false);
} catch (error) {
console.log(error);
setErrorMsg(error.message);
setIsLoading(false);
}
});
return unsubscribeAuth;
}, []);
if (isLoading) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size='large' />
<Text style={{ color: 'red' }}>Loading...</Text>
{errorMsg && <Text style={{ color: 'red' }}>{errorMsg}.</Text>}
</View>
);
}
return (
<NavigationContainer>
{user ? <HomeStack /> : <AuthStack />}
</NavigationContainer>
);
}
And on the Login screen I've got a simple function that logs the user in
const auth = Firebase.auth();
const onLogin = async () => {
try {
if (email !== '' && password !== '') {
await auth.signInWithEmailAndPassword(email, password);
}
} catch (error) {
Alert.alert(error.message)
setLoginError(error.message);
navigation.navigate('Home')
}
};
Firebase init config
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
import Constants from 'expo-constants';
// Initialize Firebase
const firebaseConfig = {
apiKey: Constants.manifest.extra.apiKey,
authDomain: Constants.manifest.extra.authDomain,
projectId: Constants.manifest.extra.projectId,
storageBucket: Constants.manifest.extra.storageBucket,
databaseURL: Constants.manifest.extra.databaseURL,
messagingSenderId: Constants.manifest.extra.messagingSenderId,
appId: Constants.manifest.extra.appId
};
let Firebase;
if (firebase.apps.length === 0) {
Firebase = firebase.initializeApp(firebaseConfig);
}
export default Firebase;
The problem is that when using the installed apk on my android I only ever see the Loading section of the RootNavigator - I've not been able to reproduce using the Expo Go app so I can't find the issue
What I expect: When I press the text "sign up" once it will both create a user in firebase auth and then create a user in firestore with the single property.
What is actually happening: When I press the text "Sign Up" it immediately creates a user in firebase auth, but then it only creates a user in firestore with the property once I press either the username field, the password field, or the Sign Up button for a second time.
I suspect: That this has to do something with promises and my nested .then's but can't figure out why it is operating this way; seems really strange.
Sample code:
import React from "react";
import * as firebase from "firebase";
import "firebase/firestore";
const Screen = () => {
const firestore = firebase.firestore();
const writeUserData = uid => {
const docRef = firestore.doc(`users/${uid}`);
docRef
.set({
example_property
})
.then(item =>
console.log("successfully added user to the collection" + item)
)
.catch(err => {
console.log(err);
});
};
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.submitButton}
onPress={() =>
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(userObj => writeUserData(userObj.user.uid))
.catch(error => {
var errorCode = error.code;
var errorMessage = error.message;
console.log("errorCode: " + errorCode);
console.log("errorMessage: " + errorMessage);
})
}
>
<Text>Sign Up</Text>
</TouchableOpacity>
</View>
);
};
It now works that I've removed the anonymous function from the .then in writeUserData. See here:
const writeUserData = uid => {
const docRef = firestore.doc(`users/${uid}`);
docRef
.set({
example_property
})
.then(console.log("successfully added user"))
.catch(err => {
console.log(err);
});
};