How firebase authentication works? - firebase

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.

Related

How to verify custom auth claim upon login?

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.

Nuxtjs, VUEX and Firebase authentication on page reload

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.

Create users with roles on client side with firebase

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

Revert auth state to last anonymous

I have a website where authentication isn't mandatory to be able to use the website.
To be able to persist user data even when not signed in, I'm using signInAnonymously() in my AppComponent.
When the user wants to register to the website, I'm following these steps:
Ask for oauth.
Check if this oauth is already in my database.
If the oauth is not already in the db, revert to the last anonymous state.
If it's already in the db, get user data.
But here is the issue: once I called signInWithPopup(), the last auth state is lost and because it's an anonymous one, I can't sign in to it again.
Because the anonymous state contains data I don't want the user to loose, I have to be able to revert to the last anonymous state if the registration process fails.
I still don't understand your use case but anyway here goes.
You sign the user anonymously on the default App instance.
var app = firebase.initializeApp(config);
app.auth().signInAnonymously()....
You then create another Auth instance with same config:
var appTemp = firebase.initializeApp(config, 'temp');
// Set persistence to none in case you forget to clear this.
appTemp.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
// Sign in the user with popup and save the credential.
var cred = null;
appTemp.auth().signInWithPopup(provider).then(function(result) {
cred = result.credential;
// Check the user email is registered in your DB.
return isRegistered(result.user.email);
}).then(function(registered) {
if (registered) {
// User is registered. Delete and relink to original anonymous.
return appTemp.auth().currentUser.delete().then(function() {
return app.auth().currentUser.linkWithCredential(cred);
});
} else {
// User not registered, just delete. The anonymous user remains.
return appTemp.auth().currentUser.delete();
}
})...
Not sure if this is exactly what you want but it will point you in the right direction using the temp auth instance copy technique.

Creating temporary anonymous users in Firebase

I'm trying auto-generate user accounts that I can save data with, and later promote those to proper username/password accounts. Any ideas on the best way to do that?
It wasn't clear to me whether or not I could switch auth providers from anonymous to password.
You can start by creating an anonymous login with Firebase, and then store that auth "uid" key to some sort of session or cookie (depending on what framework you're working with). While the client is anonymous, you can link your saved data to this anonymous "uid".
To transfer the data over to the user after they login, you'll need to use Firebase's onAuth() listener. This will notify you when the user's auth data changes. Once you have the new authenticated uid, you can link your stored data to that new uid and delete your anonymous session.
Here's the modified sample from Firebase docs:
var firebaseRef = new Firebase('https://samplechat.firebaseio-demo.com');
firebaseRef.onAuth(function(authData) {
if (authData) {
// Client is logged in, so check if it's anonymous or a real user
if (authData.provider === 'anonymous') {
// user is anonymous
// save authData.uid to some local session info, to keep track of data
} else {
// user is logged in
// transfer any data that you had stored from the anonymous session to
// this new authUser.uid
} else {
// Client is unauthenticated
// This would be a good spot to create the anonymous login
firebaseRef.authAnonymously(function(error, authData) {
if (error) {
// handle login failure
} else {
// you are now anonymously logged in, but you really don't need to do anything
// here, because your onAuth() listener is already going to catch this auth change
}
}
});
Firebase doesn't tell you what the previous uid was when the user changes their auth info, so you really need to store that anonymous uid somewhere. You could also do the whole thing without ever creating the anonymous login, by just storing session data until the user logs in, but the anonymous login provides more consistency.

Resources