Persist auth state in react/react native for Firebase - reactjs

I am using react native for an ios app and firebase for authentication. Every time I leave the app and come back, it asks for a login. I want to persist the firebase login but don't really know where to put it.
I know I need to put this in:
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL)
I have the following signIn function that runs when the login button is pressed on the signInScreen:
const signIn = async () => {
setLoading(true);
try {
await firebase.signIn(email, password);
const uid = firebase.getCurrentUser().uid;
const userInfo = await firebase.getUserInfo(uid);
const emailArr = userInfo.email.split("#");
setUser({
username: emailArr[0],
email: userInfo.email,
uid,
isLoggedIn: true,
});
} catch (error) {
alert(error.message);
} finally {
isMounted.current && setLoading(false);
}
};
I have the following signIn stuff in my firebaseContext:
const Firebase = {
getCurrentUser: () => {
return firebase.auth().currentUser;
},
signIn: async (email, password) => {
return firebase.auth().signInWithEmailAndPassword(email, password);
},
getUserInfo: async (uid) => {
try {
const user = await db.collection("users").doc(uid).get();
if (user.exists) {
return user.data();
}
} catch (error) {
console.log("Error #getUserInfo", error);
}
},
logOut: async () => {
return firebase
.auth()
.signOut()
.then(() => {
return true;
})
.catch((error) => {
console.log("Error #logout", error);
});
},
};
Where do I put the persist code I listed above from the docs?
Thanks!

When do you check if someon is signed in or not?
From the code shown it looks like you check it manuelly by calling currentUser. You have to consider that the persistance of auth state is asynchronous. That means if you call currentUser on auth before the localy saved auth state is loaded you would get there null and thing that the user is not signed in.
To get the auth state Firebase recommend to use the onAuthStateChanges event listener. With that you can listen to auth state changes no matter if you logged in or the persistet auth state is loaded.
The usage is very simple:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
That is the reson I asked where you check if someon is signed in or not. If I could see that code I could help you adopt it to use that event listener.

Related

Issues with firebase google-Oauth logout in react

I am facing an issue in my React app when implementing Google Auth using Firebase. After a successful login, I need to check if the user exists in my Firebase database. If the user does not exist, I log them out. The issue I am facing is that after logging out, I am unable to sign in again as the authentication popup does not appear. I believe this can be due to caching of the current user in the browser.
Here's my current signInWithGoogle function:
const signInWithGoogle = async () => {
const result = await signInWithPopup(auth, provider);
console.log(result.user)
const mailId = result.user.email ? result.user.email:""
const Isvalid = await validate(mailId);
if (Isvalid) {
console.log('validated user')
navigate('/dashboard')
} else {
signOut(auth).then(() => {
console.log('Signout Successful');
}).catch((err) => {
console.log('Error', err);
})
alert('not a valid user')
}
}

Firebase 9 doesn't remember logged in

I am trying to use firebase 9 auth. I already made sign in and sign up pages. It work correctly.
But I have a problem with remembering the logged in info. I want to remember the user logged info. but it ask me to sign in every time i refresh the page. I tried to check it with onAuthStateChange It sign out every refresh. this is my code for checking:
onAuthStateChanged(auth, () => {
if (user) {
console.log("LOGGED IN");
} else {
console.log("LOGGED OUT");
}
});
It log "LOGGED OUT" every refresh.
The Firebase docs says: You can use this to do remember the logging info
import { getAuth, setPersistence, signInWithEmailAndPassword, browserSessionPersistence } from "firebase/auth";
const auth = getAuth();
setPersistence(auth, browserSessionPersistence)
.then(() => {
// Existing and future Auth states are now persisted in the current
// session only. Closing the window would clear any existing state even
// if a user forgets to sign out.
// ...
// New sign-in will be persisted with session persistence.
return signInWithEmailAndPassword(auth, email, password);
})
.catch((error) => {
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
});
I tried that this is my code:
useEffect(()=> {
setPersistence(auth, browserLocalPersistence)
.then(() => {
navigate("/");
return signInWithEmailAndPassword(auth, mail, password)
})
.catch((err) => {
console.log(err.message);
pushNotification({ type: "error", message: err.message });
});
}, [])
I tried both of browserLocalPersistence and browserSessionPersistence
Please how can i make remeber logged functionality?
Excuse my english.

Firebase Passwordless Email Authentication Error in Expo App

I am setting up passwordless Auth in my Expo app using the Firebase SDK. I've gotten to the point where emails are being sent to the user's desired address with a redirect link back to the app. When the user clicks the link, they are indeed redirected but they are not being authenticated. I am receiving a generic error in the console :
ERROR: [Error: An internal error has occurred.]
But I know that my credentials are passing through properly as I have logged them out when the function runs:
isSignInWithEmailLink:true, url: exp://10.0.0.27:19000?apiKey=AIzaSyAmpd5DdsjOb-MNfVH3MgF1Gn2nT3TBcnY&oobCode=7FJTfBjM28gkn6GfBSAdgAk7wOegg9k4D5poVcylhSYAAAF8BO5gHQ&mode=signIn&lang=en
I am calling useEffect on this function:
useEffect(() => {
signInWithEmailLink();
}, []);
Send Link To Email (WORKING)
const sendSignInLinkToEmail = (email) => {
return auth
.sendSignInLinkToEmail(email, {
handleCodeInApp: true,
url: proxyUrl,
})
.then(() => {
return true;
});
};
User clicks on a link from the email to redirect to the app to Authenticate (NOT WORKING)
const signInWithEmailLink = async () => {
const url = await Linking.getInitialURL();
if (url) {
handleUrl(url);
}
Linking.addEventListener('url', ({ url }) => {
handleUrl(url);
});
};
(RETURNING ERROR)
const handleUrl = async (url) => {
const isSignInWithEmailLink = auth.isSignInWithEmailLink(url);
console.log('isSignInWithEmailLink: ', isSignInWithEmailLink, 'url', url);
if (isSignInWithEmailLink) {
try {
await auth.signInWithEmailLink(email, url);
} catch (error) {
console.log('ERROR:', error);
}
}
};
Have you enabled email sign in in your firebase console?
Are you storing the email in localStorage? It looks undefined in your logic.
Your listener should be in the useEffect hook.
I've code my code working looking like this:
const handleGetInitialURL = async () => {
const url = await Linking.getInitialURL()
if (url) {
handleSignInUrl(url)
}
}
const handleDeepLink = (event: Linking.EventType) => {
handleSignInUrl(event.url)
}
useEffect(() => {
handleGetInitialURL()
Linking.addEventListener('url', handleDeepLink)
return () => {
Linking.removeEventListener('url', handleDeepLink)
}
}, [])
You should use the onAuthStateChanged within useEffect rather than try and log the user in at that point in time. useEffect is used when you need your page to re-render based on changes.
For example:
useEffect(() => {
// onAuthStateChanged returns an unsubscriber
const unsubscribeAuth = auth.onAuthStateChanged(async authenticatedUser => {
try {
await (authenticatedUser ? setUser(authenticatedUser) : setUser(null));
setIsLoading(false);
} catch (error) {
console.log(error);
}
});
// unsubscribe auth listener on unmount
return unsubscribeAuth;
}, []);
You should invoke the user sign in method through other means such as a button to sign in, or validate user credentials at some other point within your app.
custom function:
const onLogin = async () => {
try {
if (email !== '' && password !== '') {
await auth.signInWithEmailAndPassword(email, password);
}
} catch (error) {
setLoginError(error.message);
}
};
Source: https://blog.jscrambler.com/how-to-integrate-firebase-authentication-with-an-expo-app

How to get Firestore document string to display? Can't perform state update, async function in useEffect

I have an AuthProvider/context providing firebase authentication. In a lower component I'm trying to render current user data from Firestore, such as a username, when they log in.
export function AuthProvider({ children }) {
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password)
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user);
setLoading(false);
if (currentUser) {
const docRef = db.collection("users").doc(currentUser.uid);
docRef.get().then(function(doc){
if(doc && doc.exists) {
setCurrentUsername(doc.data().username);
console.log(currentUsername)
}
}).catch(function(error) {
console.log("no document:", error);
});
} else {
// no user logged in
};
})
return unsubscribe
}, [login])
const value = {
currentUser, //firebase
signup,
login,
logout,
resetPassword,
currentFullname,
currentUsername,
currentEmail,
loading,
setLoading,
}
return (
<AuthContext.Provider
value={value}>
{children}
</AuthContext.Provider>
)
}
async function handleLogin(e) {
e.preventDefault()
try {
setError("")
setLoading(true)
await login(emailRef.current.value, passwordRef.current.value)
} catch {
setError("Incorrect email or password")
}
setLoading(false)
}
Only on logging in do I want to fetch additional user information from Firestore since Firebase authentication only has uid and email. I get the "can't perform React state update on an unmounted component" error.
Firestore data is structured like: collection("users") --> document (firebase uid) --> username: someUsername, firstname: bobSmith, etc...
Fixed! Code works for me now and has been updated to help any future people.
I had forgot to pass in a useContext value prop.
And then I changed the useEffect dependency array to login. Thanks Drew for guiding me through!

React Redux Firebase Keep user logged in function

I added my login and logout functions and its working properly, but I don't know how to keep users logged in? Any quick advices? Thanks. This is my login logout actions:
export function logout_action() {
return dispatch => {
firebase.auth().signOut()
.then(function () {
const logged_value = null;
dispatch(login({
...logged_value
}));
}).catch(function (error) {
// An error happened.
});
}
}
export function login_action() {
return dispatch => {
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider).then(function (result) {
const logged_value = result.user;
dispatch(login({
...logged_value
}));
}).catch(function (error) {
var errorCode = error.code;
});
}
}
You can save the result either on the redux store or save it in window.localstorage when the user logins in and when they logout you can delete the result from window.localstorage or the redux store.

Resources