Firebase 9 doesn't remember logged in - reactjs

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.

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')
}
}

FirebaseError: Firebase: Error (auth/invalid-value-(oob-code),-starting-an-object-on-a-scalar-field)

I am getting an error on the oob-code given by firebase, FirebaseError: Firebase: Error (auth/invalid-value-(oob-code),-starting-an-object-on-a-scalar-field)..
This is happening on my reset password function, as I customised my own reset password page instead of using firebase default.
Here are my code, I followed the code on firebase documentation closely but my error comes in at the auth.verifyPasswordResetCode(auth, actionCode).then((email) => { line shown below.
const handleResetPassword = () => {
// Verify the password reset code is valid.
try {
console.log(actionCode)
auth.verifyPasswordResetCode(auth, actionCode).then((email) => {
console.log("got in")
console.log(email)
const accountEmail = email;
// Save the new password.
auth.confirmPasswordReset(auth, actionCode, values.newPassword).then((resp) => {
// Password reset has been confirmed and new password updated.
console.log(resp);
login(accountEmail, values.newPassword)
.then((flag) => {
if (flag.includes('ERROR')) {
setMessage(flag)
}
})
.catch((e) => {
console.log(e);
setMessage("Something went wrong")
})
// TODO: Display a link back to the app, or sign-in the user directly
// if the page belongs to the same domain as the app:
// auth.signInWithEmailAndPassword(accountEmail, newPassword);
// TODO: If a continue URL is available, display a button which on
// click redirects the user back to the app via continueUrl with
// additional state determined from that URL's parameters.
}).catch((error) => {
// Error occurred during confirmation. The code might have expired or the
// password is too weak.
console.log(error)
});
}).catch((error) => {
console.log(error)
// Invalid or expired action code. Ask user to try to reset the password
// again.
});
} catch (e) {
console.log(e);
}
}
Here is the console log output for the error.
Solved!
I used the imported functions instead of the app auth.
import { verifyPasswordResetCode, confirmPasswordReset } from "firebase/auth";
verifyPasswordResetCode(auth, actionCode).then((email) => {
....
confirmPasswordReset(auth, actionCode, values.newPassword).then((resp) => {
...

Persist auth state in react/react native for Firebase

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.

How do I use a Firebase refresh token to persist authentication?

I have been trying to figure this out for weeks and either can't seem to understand the documentation, or something. I appreciate any help you can give.
I am using the Firebase SDK
I have my server-side route, in which I can access the token and could send it to the front:
const admin = require("firebase-admin")
admin.initializeApp()
exports.loginRoute = (req, res) => {
const user = {
email: req.body.email,
password: req.body.password
}
const { valid, errors } = validateLoginData(user)
if (!valid) {
return res.status(400).json(errors)
}
admin
.auth()
.signInWithEmailAndPassword(user.email, user.password)
.then((data) => {
console.log(data.user.refreshToken, "refresh token")
return data.user.getIdToken(true)
})
.then((token) => {
return res.json({ token })
})
.catch((err) => {
console.error(err)
if (err.code === "auth/user-not-found") {
return res.status(400).json({ general: "User not found" })
} else if (err.code === "auth/wrong-password") {
return res
.status(400)
.json({ password: "User credentials don't match" })
} else {
res.status(500).json({
error: "Something went wrong, please try again."
})
}
})
}
Here is where I could use the refresh token (on the front end) to fetch a new authentication token, but I can't figure out how to create a route to do this:
if (token) {
const decodedToken = jwtDecode(token)
if (decodedToken.exp * 1000 < Date.now()) {
localStorage.setItem("Authentication", false)
//axios request to persist authentication would go here
}
}
Does anyone have a route that would work, or advice on what to do?
EDIT
const login = async (credentials) => {
let token
await axios
.post("/api/login", credentials)
.then((res) => {
token = res.data.token
const FBIdToken = `Bearer ${token}`
localStorage.setItem("token", token)
localStorage.setItem("FBIdToken", FBIdToken)
localStorage.setItem("Authentication", true)
context.setAuthenticated((prev) => true)
})
.then(() => {
context.getUserData()
})
.then(() => {
context.setUserState((prevUserState) => ({
...prevUserState,
token
}))
})
.catch((err) => {
context.setUserErrors((prev) => ({
...prev,
errors: err.response.data
}))
})
history.push("/")
}
Observer (client-side):
firebase.auth().onAuthStateChanged((user) => {
if (user) {
firebase
.auth()
.currentUser.getIdToken(/* forceRefresh */ true)
.then((idToken) => {
const FBIdToken = `Bearer ${idToken}`
localStorage.setItem("FBIdToken", FBIdToken)
})
.catch((err) => {
console.log(err)
})
} else {
localStorage.removeItem("FBIdToken")
}
})
If you sign in with the Firebase Authentication JavaScript SDK in the client-side code, it already persists the user's sign-in state, and tries to restore it when you reload the page. You shouldn't have to do anything for that yourself.
It seems like you were using the same SDK in a server-side environment though, which is quite unusual. If you want to mint tokens yourself in a server-side environment, you should use the Firebase Admin SDK to do so. You can then send that token back to the client, and use it to sign in to Firebase Authentication there.
But for the vast majority of use-cases, I recommend using the Firebase Authentication SDK in your client-side code, so that the SDK managed refreshing of the token for you. If you then want to pass the token to the server, you can use getIdToken() as you do now. You can also monitor ID token generation, or more commonly monitor if a user's sign-in session is restored as shown in the first example of the documentation on detecting the current user.

Error with firebase auth upon sign-up for react app

Whenever I try to sign-up locally, I get this error:
"auth/network-request-failed", message: "A network error (such as timeout, interrupted connection or unreachable host) has occurred."}
Upon sign-up, I'm simply redirecting the user to a new page and that's all.
I read that it could be a problem with the service worker made with create-react-app, but I'm not entirely sure if it's a good idea to disable it.
This is how I'm handling sign-ups:
handleSubmit(e) {
e.preventDefault();
firebase.auth()
.createUserWithEmailAndPassword(this.state.emailValue, this.state.passValue)
.catch((error) => {
console.error(error)
})
this.handleAuthChange()
}
handleAuthChange() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
window.location = 'thank-you'
let email = user.email
console.log(email + " logged in")
} else {
window.location = ""
console.log("not logged in")
}
})
}
How should I fix this?
Your handleAuthChange() function should probably be called in the success function once sign-in is done considering you want to start the firebase.auth().onAuthStateChanged listener after the firebase.auth().createUserWithEmailAndPassword function call is made.
var self = this;
handleSubmit(e) {
e.preventDefault();
firebase.auth()
.createUserWithEmailAndPassword(this.state.emailValue,
this.state.passValue)
.then(function(userData){
self.handleAuthChange();
})
.catch((error) => {
console.error(error);
})
}
A better idea I guess could be to start the listener on page load by removing it from the function as it will maintain state of the firebase auth is case of page reload.
firebase.auth().onAuthStateChanged((user) => {
if (user) {
window.location = 'thank-you'
let email = user.email
console.log(email + " logged in")
} else {
window.location = ""
console.log("not logged in")
}
});
This listener will automatically detect if a user has logged / signed up into your app using the firebase.auth methods.
Hope this helps.

Resources