Google Firebase authentication in ReactNative App - javascript

I was developing an app which I like implements Firebase as Authenticating system.
My problem comes when I try to set up the Authentication with Google provider when I try to modify the colletion of firestore where the users are saved. My code is the following:
export const loginWithGoogle = () => {
const navigation = useNavigation();
useEffect(() => {
setTimeout(() => {
navigation.navigate('/RegisterScreen');
}, 10000);
}, []);
return () => {
return firebase
.auth()
.signInWithPopup(Providers.google)
.then(async result => {
//console.log(result.credential.accessToken);
const user = result.user;
console.log(user);
//This 2 lines below doesn't work to get the colletion.
db.('users').setItem('userid', user!.uid);
collection.(db,'users').setItem('photoURL', user!.photoURL);
//TODO if userid exists IN USERS db then use update IF NULL use set
await db.collection('users').doc(user!.uid).update({
// id: user.uid,
name: user!.displayName,
email: user!.email,
phone: user!.phoneNumber,
photoURL: user!.photoURL,
});
})
.then(() => {
navigation.navigate('ProtectedScreen');
})
.catch(err => {
console.log(err);
});
};
};
So I guess that my error comes from unknowledge of how to manage data saved on firestore.
If you can help take thanks in advance !

There are some thing we need to clear here:
You can just merge the data. There is no need to read/get it from Firestore to check if it is there and save it onyl if it's not. You will be charged for reads and writes. In the end it's cheaper to always just write without checking if something exists.
Also this code here:
db.('users').setItem('userid', user!.uid);
collection.(db,'users').setItem('photoURL', user!.photoURL);
especially with the db.( and collection.( doens't look good. Even if it is it's not for getting data but for saving it.
Could you pls clarify witch Firebase SDK you use: version 8 or 9. Also pls check a little bit the docs here.

Related

Making two requests in useEffect

I am trying to fetch some user data from Firebase using getDoc and some data from MongoDB using axios in React.js.
Code:
async function getSolvedProblems() {
const docRef = await doc(db, "users-progress", user.uid);
await getDoc(docRef).then((doc) => {
console.log(doc.data());
});
}
useEffect(() => {
//fetch user's solved problems from firebase
getSolvedProblems();
//fetch problems from db server
axios
.get(process.env.REACT_APP_BACKEND_URL)
.then((res) => {
//doing something here
})
.catch((err) => {
console.log(err);
});
}, []);
But I don't know why the firebase data is not getting logged in console, when I hit refresh. But when I make any change in code, and save it, then it gets logged. I am unable to understand how useEffect is working here.
This is use effect work on below:
useEffect(() => {
//Runs only on the first render
}, []);
Also, you need to handle the catch block in your getSolvedProblems() method, see is there any error there.
On my guess, there is no value on user.uid when you load on page render

Firebase Realtime database showing strange behavior

I am using react-native-firebase in an ejected expo app and trying to build a presence detection system in my chat app which will detect that if the message recipient is online and if not when was he/she was last online. The data will be stored as follows in firebase realtime database:
{
lastSeen:[{
[userId]:{
state: boolean
time: serverTimeStamp
}
}]
}
The problem is that firebase console never shows the data and only if recipient is online then app shows this data (even though its not visible in console) but if user is offline then nothing is returned and no error generated. I have set read and write to true in realtimeDB rules. Here is the code I am using:
import database from "#react-native-firebase/database";
export const updateUserLastSeen = (userId) => {
const userStatusDatabaseRef = database().ref("/lastSeen/" + userId);
console.log("updatelast", userId);
userStatusDatabaseRef
.set({
state: true,
time: database.ServerValue.TIMESTAMP,
})
.then(() => console.log("online"))
.catch((e) => console.log(e));
// database()
// .ref(".info/connected")
// .on("value", function (snapshot) {
// if (snapshot.val() == false) {
// return;
// }
userStatusDatabaseRef
.onDisconnect()
.set({
state: false,
time: database.ServerValue.TIMESTAMP,
})
.then(function () {
console.log("disconnect configured");
// userStatusDatabaseRef.set({
// state: true,
// time: database.ServerValue.TIMESTAMP,
// });
});
// });
};
export const checkUserLastSeen = (userId, setUserLastSeen) => {
console.log("check last", userId);
database()
.ref("/lastSeen/" + userId)
.on("value", (snapshot) => {
setUserLastSeen(snapshot.val());
console.log("User data: ", snapshot.val());
});
console.log("after check last");
};
I tried both the code from firebase docs and rnfirebase docs. In above code, none of the "then" or "catch" functions get called in updateUserLastSeen but in checkUserLastSeen "on" is invoked only if bearer of userId is online. Also, I am using realtime db only for this purpose and cloud firestore for other data storing and its working fine.
Any help would be appreciated. Thanks.
If neither then nor catch of a write is called, it typically means that the client is not connected to the server.
I recommend checking to make sure your app has a network connection, and that you've configured the (correct) URL for your database.

Create a firestore doc for each auth user in Nextjs (only using sign in with Google)

I am building a user auth system with Nextjs
I am trying to create a document within firestore for each user in my firebase authentication system. I was easily able to do this in previous projects when creating an account with email and password but with the 'sign in with google' feature I can't seem to figure out how.
I don't want to create a new document every time the user logs in..
My only idea is this:
When user signs in, loop through all firestore documents and see if the users e-mail matches any firestore doc email. If not, create document, else return.
I feel like there is another way though..
Simplest way would be to make a custom hook that can be used anywhere across the application.
First in the _app file inside useeffect hook simply try to get the data from doc if data exist well it means user document is already there and if data does not exists, we need to create a document for that, quite simple. Let's see the code now
Make sure you read comments written inside the code to better understand
In _app.js,
useEffect(async () => {
// now this checks if user is logged in or not
const unsubscribe = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
// if logged in it simply passes the userAuth object to handle user profile
// which is a custom hook to check if document for this user pre-exist or not!
// if there wont be any document it will go and create a document and return
// that document.
// If already there is a document created it will simply return that.
const userRef = await handleUserProfile(userAuth);
userRef.onSnapshot((snapshot) => {
// later you can save currentUsr value in any of the state to use it later
const currentUsr = {
id: snapshot.id,
...snapshot.data(),
};
}
});
}
}
});
return () => unsubscribe();
}, []);
Now the custom hook to check if document is already there or not, here comes the tricky part.
export const handleUserProfile = async (userAuth) => {
// as a second check it check if falsy values are returned
if (!userAuth) return;
const { uid } = userAuth;
// first it tries to get data from that uid
const userRef = firestore.doc(`users/${uid}`);
const snapshot = await userRef.get();
// checks if snapshot exist
if (!snapshot.exists) {
// if snapshot does not exist, it will simply create a document with document
// name as this 'uid'
const { displayName, email } = userAuth;
const timeStamp = new Date();
try {
// making use of same userRef that we created above to create
await userRef.set({
displayName,
email,
createdAt: timeStamp,
});
} catch (error) {}
}
// if snapshot exist it will simply return the userRef which contains the
// document only.
return userRef;
};
Voila! :)
There is no reason why you should not use the onAuthStateChanged event on auth. A write would cost you the same as a read to check if the data is already there. But with a read you would sometimes need also a write. In total only writes every time come less expensive in read/write actions.
Just listen to auth state changes and update your firestore data each time it changes:
firebase.auth().onAuthStateChanged(async (user) => {
if (user) {
await firebase.firestore()
.collection("users")
.doc(user.uid)
.set(data, {merge:true});
// User is signed in.
}
});
Make sure to use set with merge turned on. That will ensure that the data will be created if it doens't exist and update only the field you want to update.
Also make sure to store the data under the user uid. With that you ensure that each user has an unique idenfier. It is a bad practice to store users under the email. One of the reasons for that is that emails could have chars that are not supported as keys so would need to remove those when saving and add them again when reading the keys.
Firestore won't create duplicate docs if created when signing in with Google.. so this works:
const signInWithGoogle = () => {
fire
.auth()
.signInWithPopup(google_provider)
.then((result) => {
/** #type {firebase.auth.OAuthCredential} */
var credential = result.credential;
// This gives you a Google Access Token. You can use it to access the Google API.
var token = credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
})
.catch((error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
})
// CREATE USER DATA IN FIRESTORE
.then(async () => {
const data = {
//ADD DATA HERE
};
await fire
.firestore()
.collection("users")
.doc(fire.auth().currentUser.email)
.set(data);
});
};

Set on firebase and then set firebase claims

So i working with firebase auth and database in order to set new user to data base, if set successful i want to set claims for that user.
So it means i have a promise within a promise:
function setUser(user){
// no need for the database code before this, but userRef is set properly
return userRef.set(user)
.then(succ => {
return firebase.firebase.auth().setCustomUserClaims(user.key, {admin: true})
.then(() => {
console.log("setting claims")
return true;
});
})
.catch(err => {
return err
})
}
calling function:
app.post("/register_user",jsonParser,async (req, res) => {
var user = req.body.user;
let result = await fireBase.setUser(user);
res.send(result);
})
What happens is that i get the set on the database but claims are not set nor i can i see the log. I know its a js question and not firebase one. I tried many different ways (with await) but non worked.
firebase.firebase does not seem correct. You need to be using the admin object which can be initialised using const admin = require('firebase-admin'); This is not part of the firebase db sdk, but the admin one. You can also use the userRef.uid as that gives you the id of the document of the user, if that is what you want, else use your user.key
return admin.auth().setCustomUserClaims(userRef.uid, {
admin: true
}).then(() => {
//on success
});

set display name while creating user with firebase

I have a react app with which I want to handle authentication with firebase.
My code successfully signs up and logs in but i am trying to add extra information on sign up but i have not been successful. I have tried answers [here]: Firebase v3 updateProfile Method and [here]: Firebase user.updateProfile({...}) not working in React App
But they don't seem to work. Below is my code
const SignUp = ({ history }) => {
const handleSignUp = useCallback(
async event => {
event.preventDefault();
const { email, password } = event.target.elements;
try {
let cred = await app
.auth()
.createUserWithEmailAndPassword(email.value, password.value);
await cred.user.updateProfile({
displayName: 'hello'
});
history.push('/');
} catch (error) {
console.log(error);
}
},
[history]
);
Please how do i fix this because currently on the email and username sets? Thanks
In order to change user profile you should use firebase.auth().onAuthStateChanged() function, as follows:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
Then you can get others user properties. Here you can find all info you need. https://firebase.google.com/docs/auth/web/manage-users. Hope it helps.

Categories

Resources