I am using Firebase authentication in my Nuxt application, but I can't figure out a good way to persist the user logged-in state in my Store.
I am logging users in by dispatching a async action in my store. when a user refreshes the browser (reloads it), the user will be logged out in my app, but will remain logged in in Firebase (as they didn't log out). It's a problem because the user will have to sign into my app again in order to view restricted pages and perform certain actions.
I tried to grab the user in NuxtServerInit (from the index.js store as I am using modules mode) from:
firebase.auth().currentUser
But it it returns null, and I also tried to listen for:
firebase.auth().onAuthStateChanged((user)
But it also returns null
I also tried creating a middleware to check if a user is currently logged in, but it also returns null.
In a previous Vue project I was able to do it in the in the main.js file created-hook like this:
new Vue({
router,
store,
render: h => h(App),
created () {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
store.dispatch('autoSignIn', user)
}
})
}
}).$mount('#app')
But I am not able to figure out how to do the same in my Nuxt application.
I appreciate any ideas on how to solve the problem keep my users logged in.
Related
Similar to Uber, I have two applications, one for clients and one for drivers. Is it possible to know which role type the user has upon login? For instance, if I have a client account and I try to log in on the driver's application I should get the error: "client accounts cannot be used to log into the driver application".
Let's say I stored the user's account type (driver or client) in a custom auth claim, would it be possible to access that while firebase auth is verifying the email and password, or does the user have to log in successfully before I can verify the value of the custom auth claim?
Essentially, if the user tries logging into the wrong application, I want it to come back as an error without actually logging them in. So far I've only been able to check for this after the user logs in using getIDTokenResult.
Any help is appreciated! :)
Essentially, if the user tries logging into the wrong application, I want it to come back as an error without actually logging them in.
You seem to be mixing authentication (the user enters credentials that prove who they are) with authorization (the user is allowed to do certain things based on who the are). Firebase Authentication solely is concerned with the former: allowing the user to sign in once they enter the correct credentials for their account. Once the user is signed in, your application code can then determine whether they're allowed to perform certain actions.
For your specific use-case for example, the idiomatic approach is to:
Sign the user in to Firebase Authentication.
Check whether their token contains the necessary claim for the app they're trying to use.
If so, allow them to continue to the main screen of your app.
If not, inform them of that fact and don't allow them to continue.
As you can see here, it is your application logic that handles all authorization logic, while Firebase takes care of the authentication.
The user must be logged in before checking the claims and anyways you cannot prevent anyone from logging in if it's a same firebase project. You should check the claim after login and if the user has logged into wrong application, just force them to logout. Security Rules can be used to prevent unauthorized access.
firebase.auth().signInWithEmailAndPassword().then(async ({user}) => {
const claims = await user.getIdTokenResult()
// check for claim
// if not valid then logout or redirect to relevant pages
await firebase.auth(can ).signOut()
})
You can show your error alerts after signing out.
If you really want to check the claim before logging the user in then you would have to use cloud functions which checks claims for the entered email but this method may not be useful in other sign in providers such as Google or Facebook.
Although I won't recommend using Cloud functions just to check the claims before users logs in as it just can be bypassed on the frontend and as mentioned above, forcing the user to logout should be enough. But here's a cloud function you can use to check the claims.
exports.checkClaim = functions.https.onCall((data, context) => {
const {email} = data;
return admin
.auth()
.getUser(uid)
.then((userRecord) => {
const {customClaims: {driver, client}} = userRecord;
if (driver) return {role: "driver"}
if (client) return {role: "client"}
return {error: "No role found"}
})
.catch((error) => {
console.log('Error fetching user data:', error);
});
});
Then call the function before you run the signInWithEmailAndPassword method.
const checkUserRole = firebase.functions().httpsCallable('checkClaim');
checkUserRole({ email: "user#domain.tld" })
.then((result) => {
const {role, error} = result;
if (error) {
alert("Something went wrong. No roles found")
} else {
console.log(`Your role is: ${role}`)
}
});
Again as mentioned above this sounds a bit overkill but if it's necessary or you prefer to do it that way then you use this function.
I'm using the Firebase web sdk (JS) in a ReactNative app. My login is simple:
firebase.auth().signInWithEmailAndPassword(email, password)
it works fine. However, if the user backgrounds my app and comes back a few hours later, they are logged out (without them having explicitly logged out and without my app logging them out). There seems to be a 1 hour timeout on the login token but Firebase is NOT refreshing this token when the app is backgrounded or when the app comes back to the foreground.
This is a bad experience for my users since they are forced to login again when they come back the next day.
How can I force the Firebase SDK to refresh the login token even if the app is backgrounded or when it comes back into the foreground?
Or can I refresh it myself somehow when the app comes back to the foreground after a few hours?
Thanks!
Authentication state is automatically persisted across application restarts. But it will have to be loaded, and verified against the server, since the ID token expires each hour
My initial guess would be that the user state simply hasn't been restored yet, which you solve by using an auto state listener.
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
... user is signed in
}
else {
... user is NOT signed in, show login screen
}
})
Also check out this checking the current authentication state in this blog post, which has an example of how to use the above approach in your React Native code.
This was entirely my fault. I had a line in my App.js render method like:
// Should have been wrapped in an if
store.dispatch(FirebaseAuth.signOutCurrentUser());
// XXX TODO login as known user for testing, DO NOT USE IN PRODUCTION
const autoLogoutAndLoginAsTestAccount = false;
if (__DEV__ && autoLogoutAndLoginAsTestAccount) {
store.dispatch(FirebaseAuth.signInWithEmailAndPassword({ email: "joel#test.com", password: "REDACTED" }));
}
which should have been
// XXX TODO login as known user for testing, DO NOT USE IN PRODUCTION
const autoLogoutAndLoginAsTestAccount = false;
if (__DEV__ && autoLogoutAndLoginAsTestAccount) {
store.dispatch(FirebaseAuth.signOutCurrentUser());
store.dispatch(FirebaseAuth.signInWithEmailAndPassword({ email: "joel#test.com", password: "REDACTED" }));
}
I had this in place so I wouldn't need to login each time I hot reloaded the RN code but although I had wrapped the autologin inside an if to prevent it from running in prod/testflight, I had not wrapped the autologout. So Firebase was doing what I asked it to and logging out the user every time it re-rendered the app (e.g. on restart).
I was using an auth listener and that was not the issue.
Sorry, this was a foolish mistake on my part and I feel embarassed since I'm normally very careful with my code and I just missed this. I didn't mean to waste anybody's time and thanks all for your careful answers. I'm posting this here in case it helps somebody in future.
I'm trying to do my first login and authentication with firebase and I need three roles
Student, teacher, admin
My NavBar should render different things depends the role of the user signed in and I've seen that there is a way to use roles with firebase https://firebase.google.com/docs/auth/admin/custom-claims
but if I'm not missunderstanding it, it should be done on the backend (I'm using spring)
I should know the role at every moment on client side How could I do it?
You can't create custom claims on the client. That would be a security hole, because users could just give themselves access to anything simply by modifying your code. You need to use the Admin SDK on the server for that.
There is also documentation for accessing custom claims on the client. You use getIdTokenResult() on the Firebase auth user object. For example:
firebase.auth().currentUser.getIdTokenResult()
.then((idTokenResult) => {
// Confirm the user is an Admin.
if (!idTokenResult.claims.admin) {
// Show admin UI.
showAdminUI();
} else {
// Show regular user UI.
showRegularUI();
}
})
.catch((error) => {
console.log(error);
});
Once I execute
firebase.auth().signInWithEmailAndPassword(email, password).then((user) => {
// here I save user to redux and set loggedIn to true
});
I am logged in. What next? My suggestions...
After login I go to main flow of App and in App.js I execute onAuthStateChanged(). And if I still get user I am still logged in, if not I set loggedIn key to false and redirect user to Login screen.
Next, I am using Redux persist to save loggedIn key between different launches of App.
But when (except I log out by myself) I'll be logged out? What period of life of auth session (if it exists) on firebase side? How it works?
.onAuthStateChanged() drives the sign-in state of your app. That's how it works.
firebase.auth().onAuthStateChanged(function (user) {
if(user){
//unhide non-public elements
} else {
//hide public elements
}
});
You then use Firebase Security Rules to control who has access to what information.
I am using Firebase 3.4.1 on an iOS React Native app. I use anonymous auth to ensure that users can only read/write their own data (i.e. security rules check auth && auth.uid).
On first run, I call firebase.auth().signInAnonymously(), and on subsequent app opens, I set up the listener
firebase.auth().onAuthStateChanged(user => {
if (user) {
// success
} else {
// something is wrong
}
}, (err) => {
// log error
});
I have noticed that a few users hit the something is wrong branch above, where the user object returned on auth change is null. I suspected this may be some intermittent failure to reauth against Firebase, but it seemed like once someone experienced this error, it was permanent.
In an effort to replicate, with my own user account that had been granted anonymous auth, I disabled the auth credential from the Firebase dashboard, which would prevent me from reauthing. When I then try to reauth, I too hit the something is wrong branch above, as expected (surprisingly, the error function is not called). However, after I flip my credential back to enabled on the dashboard, I can no longer successfully reauth and continue to receive a null user object on auth change.
Is there any way to reauth once it's failed once?