I am trying to login with phone number with firebase signInWithPhoneNumber() method for login. In which i have checked whether user auth state has been change or not. If user auth is change then login and redirect to home page. but i m getting auth null
onLoginBtnClicked() {
const { contact, password } = this.props;
const error = Validator('password', password) || Validator('contact', contact);
if (error !== null) {
Alert.alert(error);
} else {
console.log('else');
// this.props.loginUser({ contact, password});
const mobileNo = '+91'+contact;
firebase.auth().signInWithPhoneNumber(mobileNo)
.then(data => console.log(data),
firebase.auth().onAuthStateChanged((user) => {
console.log('user'+user);
if (user && !CurrentUser.isFirstTimeUser) {
const userRef = firebase.database().ref(`/users/`);
userRef.on("value", (snapshot) => {
console.log(snapshot.val());
snapshot.forEach(function(item) {
var itemVal = item.val();
if(itemVal.mobile == contact){
NavigationService.navigate('Home');
}
});
}, (errorObject) => {
console.log("The read failed: " + errorObject.code);
});
//NavigationService.navigate('Home');
}
})
)
.catch(error => console(error.message) );
}
}
There are two things to note here
onAuthStateChanged is a listener which listen for the user auth changes.
signInWithPhoneNumber sends the code to the user's phone, you have to confirm it to authenticate the user.
You need to add the listener in the react lifecycle for the component once it is mounted and remove it when it is unmounted
componentDidMount() {
this.unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({ user: user.toJSON() });
} else {
// Reset the state since the user has been logged out
}
});
}
componentWillUnmount() {
if (this.unsubscribe) this.unsubscribe();
}
// Send Message here
firebase.auth().signInWithPhoneNumber(mobileNo)
.then(confirmResult => this.setState({ confirmResult })
.catch(error => // handle the error here)
// Authenticate User typed Code here
const { userCode, confirmResult } = this.state;
if (confirmResult && userCode.length > 0) {
confirmResult.confirm(codeInput)
.then((user) => {
// handle user confirmation here or in the listener
})
.catch(error => // handle the error here)
}
Related
I am building an app with firebase .
I had successfully implemented a function that will enable the user to upload a pic to firebase storage
here it is
const uploadImageToBucket = async () => {
let blob;
try {
setUploading(true);
blob = await getPictureBlob(image);
const ref = await storage.ref().child(uuid.v4());
const snapshot = await ref.put(blob);
return await snapshot.ref.getDownloadURL();
} catch (e) {
alert(e.message);
} finally {
blob.close();
setUploading(false);
}
};
the problem is that I want the picture to be uploaded based on certain user and I want to set that pic as user profile pic .any suggestion please!!
here the user sign up function
const handleSignUp = () => {
setErrortext("");
if (!FullName) return alert("Please fill Name");
if (!Email) return alert("Please fill Email");
if (!Password) return alert("Please fill Address");
setIsLogged(true);
firebase
.auth()
.createUserWithEmailAndPassword(Email, Password)
.then((user) => {
alert("Registration Successful. Please Login to proceed");
console.log(user);
if (user) {
firebase
.auth()
.currentUser.updateProfile({
displayName: FullName,
})
.then(() => navigation.replace("Log_In"))
.then(() => {
firebase.auth().onAuthStateChanged((userData) => {
setuserData(userData);
});
})
.catch((error) => {
console.log(error);
setErrortext(error);
});
}
})
.catch((error) => {
if (error.code === "auth/email-already-in-use") {
setErrortext("That email address is already in use!");
setIsLogged(false);
} else {
setErrortext(error.message);
setIsLogged(false);
}
});
};
You can simply use updateProfile method to update currently logged in user's photoURL and set it to the download URL just requested. The uploadImageToBucket function returns that URL back so you can try this:
uploadImageToBucket().then(async (photoURL) => {
const user = firebase.auth().currentUser
await user.updateProfile({ photoURL })
console.log("Photo URL updated")
})
I have login code in react native using firebase and google signin auth.
So when new user sign in using google account, I set new data. And if user has signed in before, user go to main page.
My problem is when new user sign in > code start to get signInWithCredential > set new data user, before set data finish, onAuthStateChanged was detect there is change in auth and start to get user document / data. But because it's not finish yet, it throw error 'Can Not Get UID / Undefined UID'.
This is my login page code:
const _signIn = async () => {
setInitializing(true);
try {
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
const credential = auth.GoogleAuthProvider.credential(
userInfo.idToken,
userInfo.accessToken,
);
await auth()
.signInWithCredential(credential)
.then(response => {
const uid = response.user.uid;
const data = {
uid: uid,
email: userInfo.user.email,
fullname: userInfo.user.name,
bio: 'Halo!! ..',
username: uid.substring(0, 8),
};
const usersRef = firestore().collection('users');
usersRef
.doc(uid)
.get()
.then(firestoreDocument => {
if (!firestoreDocument.exists) {
usersRef
.doc(data.uid)
.set(data)
.then(() => {
setInitializing(false); return;
})
.catch(error => {
setInitializing(false);
Alert.alert(JSON.stringify(error.message));
});
} else {
setInitializing(false);
return;
}
})
.catch(error => {
Alert.alert(JSON.stringify(error.message));
console.log('Error getting document:', error);
return;
});
});
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
setInitializing(false);
Alert.alert('Sign in canceled');
} else if (error.code === statusCodes.IN_PROGRESS) {
setInitializing(false);
Alert.alert('Signin in progress');
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
setInitializing(false);
Alert.alert('PLAY_SERVICES_NOT_AVAILABLE');
} else {
setInitializing(false);
Alert.alert(JSON.stringify(error.message));
}
}};
And this is my index page code to check auth user:
useEffect(() => {
try {
NetInfo.fetch().then(state => {
if(state.isConnected === false){
Alert.alert('No Internet Connection Detected');
setInitializing(false);
return;
}
});
setInitializing(true);
await auth().onAuthStateChanged(user => {
if (user) {
const usersRef = firestore().collection('users');
usersRef
.doc(user.uid)
.get()
.then(document => {
const userData = document.data().uid;
setisLogin(userData);
})
.then(() => {
setInitializing(false);
})
.catch(error => {
setInitializing(false);
Alert.alert(JSON.stringify(error.message));
});
} else {
setInitializing(false);
}
});
} catch (error) {
Alert.alert(error);
} }, []);
How to wait auth().signInWithCredential finish? Thankyou.
If you need to perform more actions such read data from database or so after the user logs in, you should ideally unsubscribe from onAuthStateChanged. Essentially it won't trigger when the auth state changes (i.e. user logs in) and let you do your own custom actions. Once your processing is done, then you manually redirect the user to where the onAuthStateChange would have redirected is the user wa s logged in.
const authStateListenter = await auth().onAuthStateChanged(user => {
//...
})
// Unsubscribe auth state observer when _signIn function runs
const _signIn = async () => {
setInitializing(true);
authStateListenter()
}
Calling authStateListener will disable the auth state observer. It's similar to detaching Firestore's listeners.
I'm working on a react native project and I've came to a part where initially I implemented google sign in my project using react-native-google-signin and later on Facebook sign in using react-native-fbsdk packages with the help of firebase and both worked like a charm "individually".
The Problem
Let's say the user logged in using google account and it worked but later logged in using Facebook with the same account (I'm allowing only one email per user in firebase), I get an error
auth/account-exists-with-different-credentials
I want the user to be able to login using Facebook from the login screen or to be more specific to link his account from the login screen.
What have I tried?
I searched online and found some answers and got up with this solution or piece of code:
facebookSignin: async () => {
const result = await LoginManager.logInWithPermissions([
'public_profile',
'email',
]);
if (result.isCancelled) {
alert('User cancelled the login process');
this.setState({loginInProcess: false});
}
const data = await AccessToken.getCurrentAccessToken();
if (!data) {
alert('Something went wrong obtaining access token');
this.setState({loginInProcess: false});
}
const facebookCredential = auth.FacebookAuthProvider.credential(
data.accessToken,
);
await auth()
.signInWithCredential(facebookCredential)
// The problem starts here from the catch block
.catch((error) => {
if (
error.code === 'auth/account-exists-with-different-credential'
) {
var pendingCred = error.credential;
var email = error.email;
auth()
.fetchSignInMethodsForEmail(email)
.then(async (methods) => {
if (methods[0] === 'google.com') {
const {idToken} = await GoogleSignin.signIn();
const googleCredential = auth.GoogleAuthProvider.credential(
idToken,
);
auth()
.signInWithCredential(googleCredential)
.then((user) => {
user.linkWithCredential(pendingCred);
})
.catch((error) => console.log(error));
}
});
}
});
}
This code implements a function when triggered, if there is no user with the same email, it proceeds normally, however if there is an error (mentioned above), it will grant the user with a list of google accounts that are present in the user phone (google thing) and when he chooses his account (linked with google account) it doesn't work. The email isn't linked.
To be more specific, I would like somehow to not grant the user with all his google accounts but only with the email to be linked var email = error.email; (in the code snippet above) and for the Facebook provider to be linked successfully.
After a little of hard work, I've managed to make it work in react native and I'm gonna leave the answer here for peeps who are facing the same issue. Be ware that I used react-native-prompt-android to ask the user for confirming his password when trying to link with Facebook.
The user tries to sign with Facebook and gets this error:
auth/account-exists-with-different-credentials
This is how I handled it:
.catch((error) => {
// Catching the error
if (
error.code === 'auth/account-exists-with-different-credential'
) {
const _responseInfoCallback = (error, result) => {
if (error) {
alert('Error fetching data: ' + error.toString());
} else {
setEmail(result.email);
}
};
// Getting the email address instead of error.email from Facebook
const profileRequest = new GraphRequest(
'/me?fields=email',
null,
_responseInfoCallback,
);
new GraphRequestManager().addRequest(profileRequest).start();
if (email) {
auth()
.fetchSignInMethodsForEmail(email)
.then(async (methods) => {
// Checking the method
if (methods[0] === 'password') {
// Prompting the user to confirm/input his password for linking
const AsyncAlert = () => {
return new Promise((resolve, reject) => {
prompt(
'Password Confirmation',
'The email address is already linked with password account. Enter your password to process',
[
{
text: 'Cancel',
style: 'cancel',
},
{
text: 'Continue',
onPress: (password) =>
resolve(setPassword(password)),
},
],
{
type: 'secure-text',
cancelable: false,
placeholder: 'Password',
},
);
});
};
// Here the linking goes
await AsyncAlert().then(async () => {
await auth()
.signInWithEmailAndPassword(email, password)
.then(() => {
return auth().currentUser.linkWithCredential(
facebookCredential,
);
})
.catch(() => alert('Something went wrong'));
});
} else if (methods[0] === 'google.com') {
const {idToken} = await GoogleSignin.signIn(email);
const googleCredential = auth.GoogleAuthProvider.credential(
idToken,
);
await auth()
.signInWithCredential(googleCredential)
.then(() => {
return auth().currentUser.linkWithCredential(
facebookCredential,
);
});
}
});
} else {
alert('Something went wrong');
}
}
});
Iam building an App with a signup and login feature but can't get to the login page after signing up.
I have tried to use react navigation as below
handleSignUp = () => {
firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password)
.then(function() {
// Sign-out successful.
this.props.navigation.navigate("logn")
})
// .then(signout=>{
// })
.catch(error => this.setState({ errorMessage: error.message }));
};
This leads me to the home page.
Register screen movement below member registration section.
handleSignUp = async () => {
const result = await firebase.auth().createUserWithEmailAndPassword(this.state.email, this.state.password).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode == 'auth/weak-password') {
this.setState({ errorMessage: 'The password is too weak.' })
} else {
this.setState({ errorMessage: errorMessage })
}
});
if (result) {
this.props.navigation.navigate("logn")
}
}
I'm new with react-native-firebase
I want to link the user after login with facebook provider and google provider
I tried all solutions on the internet but any of them worked.
this is my code
const loginUser = await firebase.auth().signInAndRetrieveDataWithEmailAndPassword('test#gmail.com','password888').then(async function(userRecord) {
console.log("Successfully sign in user:", userRecord.user._user);
let user = firebase.auth().currentUser;
console.log('current user ',user)
let linkAndRetrieveDataWithCredential=firebase.auth().currentUser.linkAndRetrieveDataWithCredential(firebase.auth.FacebookAuthProvider.PROVIDER_ID).then(async u=>{
console.log('linkAndRetrieveDataWithCredential u',u)
}).catch(async (e)=>{
console.log('linkAndRetrieveDataWithCredential error',e)
})
console.log('linkAndRetrieveDataWithCredential error',linkAndRetrieveDataWithCredential)
/**/
await firebase.auth().fetchSignInMethodsForEmail('sss#sss.sss')
.then(async providers => {
console.log('login index providers',providers)
}).catch(function(error){
console.log('login index providers error',error)
})
}).catch(async function(error){
console.log('login error',error,error.email)
if(error.code=='auth/user-not-found'){
}else if(error.code=='auth/wrong-password'){
errorMsg=`${L('password')} ${L('notValid')}`
}
if(errorMsg){
if (Platform.OS === 'android') {
ToastAndroid.show(
errorMsg,
ToastAndroid.LONG
)
} else {
Alert.alert(
'',
errorMsg,
[{ text: L('close'), style: 'cancel' }]
)
}
}
console.log("Error sign in user:", error.code);
})
linkAndRetrieveDataWithCredential needs an AuthCredential, in my app I use react-native-fbsdk to get the credential(You’ll need to follow their setup instructions).
This function will prompt the user to log into his facebook account and return an AccessToken, then you get the credential from firebase and finally linkAndRetrieveDataWithCredential.
linkToFacebook = () => {
LoginManager.logInWithReadPermissions(['public_profile', 'email'])
.then((result) => {
if (result.isCancelled) {
return Promise.reject(new Error('The user cancelled the request'))
}
// Retrieve the access token
return AccessToken.getCurrentAccessToken()
})
.then((data) => {
// Create a new Firebase credential with the token
const credential = firebase.auth.FacebookAuthProvider.credential(data.accessToken)
// Link using the credential
return firebase.auth().currentUser.linkAndRetrieveDataWithCredential(credential)
})
.catch((error) => {
const { code, message } = error
window.alert(message)
})
}