How do I properly use onAuthStateChanged from firebase in react native? - firebase

I'm a bit confused over the firebase function onAuthStateChanged().
componentDidMount() {
fbAuth.onAuthStateChanged(function(user) {
if (user) { //THIS TRIGGERS BOTH AT LOGIN AND REGISTRATION
console.log("LOGGED IN");
} else {
//TRIGGERS WHEN LOGGING OUT; NOT WHEN FAILING TO LOGIN!
console.log("LOGGED OUT");
}
});
}
I thought that the if(user) block triggered when the user has logged in, but the console.log is also triggered when a new account is created. How do I make a conditional that only triggers at login (and not by creating a new account?)

onAuthStateChanged(nextOrObserver, error, completed) returns function()
returns a listener function
Therefore you need to listen it when the component is mounted and unlisten when the component is unmounted
You need to make a separate Component if you want to listen specifically for Login
Login.js // Or whatever your login component is
componentDidMount() {
// Bind the variable to the instance of the class.
this.authFirebaseListener = firebase.auth().onAuthStateChanged((user) => {
this.setState({
loading: false, // For the loader maybe
user, // User Details
isAuth: true
});
});
}
componentWillUnmount() {
this.authFirebaseListener && this.authFirebaseListener() // Unlisten it by calling it as a function
}

Related

Firebase Authentication JS does not populate `providerData`array

in a VueJS / QuasarJS application Im using firebase-js-sdk [1] together with firebaseui-web [2] to handle authentication.
After successful auth with any of the configured providers (e.g. password, google, apple, etc) I want to check which provider the user used. But immediately after successful authentication the user.providerData[] array that should contain the information is empty.
BUT if I reload my app the user.providerData[] array is suddenly populated correctly.
Iยดm checking for user data with something like this
import { getAuth } from "firebase/auth";
const auth = getAuth();
const user = auth.currentUser;
if (user) {
console.log(user.providerData)
}
After that the user object is fully populated (incl auth tokens, etc) but the user.providerData[] array is empty. Only after a page reload (CTRL-R) does the array get populated.
I searched both projects issues pages and documentation and didnt find anything that could explain this.
Im thankful for ANY idea where to look next!
EDIT
As suggested by #aside Im using onAuthStateChanged to check for updates of the user state.
onAuthStateChanged(
fbAuth,
(user) => {
if (user) {
console.log("onAuthStateChanged: user found");
console.log("onAuthStateChanged: user.providerData", user.providerData);
console.log("onAuthStateChanged: user", user);
} else {
console.log("onAuthStateChanged: no user found");
}
},
function (error) {
console.log("onAuthStateChanged:", error);
}
);
But even if I wait minutes after authentication is completed, still the user.providerData array is only populated after a page reload.
Here is a full demo: https://codesandbox.io/s/github/perelin/firebase-auth-providerdata-test
Thanks in advance :)
Im using
"firebase": "9.6.1",
"firebaseui": "6.0.0",
[1] https://github.com/firebase/firebase-js-sdk
[2] https://github.com/firebase/firebaseui-web
Your app should call getAuth().currentUser.reload() to refresh the local user data after login.
This could be done either in beforeRouteEnter() nav guard of the LoggedIn view:
// LoggedIn.vue
import { getAuth, signOut } from "firebase/auth";
export default {
async beforeRouteEnter(to, from, next) {
await getAuth().currentUser?.reload() ๐Ÿ‘ˆ
next()
},
}
demo 1
Or in the onAuthStateChanged callback:
// main.js
onAuthStateChanged(
fbAuth,
async (user) => {
await user?.reload() ๐Ÿ‘ˆ
},
)
demo 2
Your code is only running once instead of running every time the auth state is updated.
If you want to listen to any changes to the auth state, use a callback along with onAuthStateChanged as described here.
https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed-in_user
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// Check used provider here
const providerData = user.providerData;
// ...
} else {
// User is signed out
// ...
}
});
The reason checking/requesting the user object right after authentication does not work is that it might take firebase a second to update the providerData array. signInWithX might therefore return before the property is updated.

NativeScript Vue - Firebase Re-login the user on app restart

I'm using NativeScript-Vue with Firebase authentication which is all working fine.
What I'm trying to do is to route the user directly to the Home component if they are already authenticated with Firebase or if they aren't direct them to the Log In component.
As per my sample code I've passed the onAuthStateChanged parameter when initializing Firebase to check if the user is already authenticated and save the state in isLoggedIn. Then when rendering the Vue instance I check the truthy state of isLoggedIn to determine weather to send them to Home or Log In.
I believe that the Vue instance renders first before the onAuthStateChanged is processed according to my console.
I'll be grateful if someone can provide a pointer.
let isLoggedIn = false
firebase.init({
onAuthStateChanged: data => {
console.log(data.loggedIn ? "Logged in to firebase" : "Logged out from firebase");
if (data.loggedIn) {
isLoggedIn = true
}
else {
isLoggedIn = false
}
}
}).then(
function () {
console.log("firebase.init done");
},
function (error) {
console.log("firebase.init error: " + error);
}
);
// Prints Vue logs when --env.production is *NOT* set while building
Vue.config.silent = (TNS_ENV === 'production')
new Vue({
render: h => h('frame', [h(isLoggedIn ? Home : LogIn)])
}).$start()

Nuxtjs and Firebase Auth: await firebase.auth().currentUser not waiting?

Nuxt.js is focuses on server side rendering and has an asyncData property that is called once before the page component is loaded.
I am trying something like:
async asyncData({params}) {
// firebase.auth().onAuthStateChanged((user)=>{ // <-- this doesn't work in the asyncData property
let user = await firebase.auth().currentUser
let info = {}
console.log(user)
user.uid === null // true
}
Two similar questions:
firebase.auth().currentUser is null
Get firebase.auth().currentUser with async/await
have solutions which do not seem to work with nuxt...
I have also tried:
function getCurrentUser(auth) {
let userLoaded = false;
return new Promise((resolve, reject) => {
if (userLoaded) {
resolve(firebase.auth().currentUser);
}
const unsubscribe = auth.onAuthStateChanged(user => {
userLoaded = true;
unsubscribe();
resolve(user);
}, reject);
});
}
It seems like onAuthStateChanged is triggered on client-side only. But the thing is, SSR functionality would make sense only for non-authenticated users, for authed-user scenario might as well just put the firebase call logic into mounted hook.

Using Firebase user UID with vuefire manual binding

In a simple SPA with Vue and Firebase, there are two routes: Login and Chat.
Upon login, the user is redirected to the Chat route where Firebase database bindings are done manually using vuefire's $bindAsArray(), inside the created() life-cycle hook. This is because the bindings require the uid assigned by the Firebase authentication to be available.
This works fine, until the user refreshes the page. If auth().currentUser is used to get the uid, it returns null. If the auth().onAuthStateChanged() watcher is used, Vue attempts to render the component before the Firebase database bindings are done. What am I missing?
I come across this scenario, as workaround I use component wrapper that has UID as property, if UID is null show a waiting message/animation else show your original component.
My real scenario is a little more complex to post it here (firebase, routing, vuex) but basically that wrapper component should look similar to this
<template>
<component :is="CurrentComponent" />
</template>
<script>
import App from './App';
import WaitingAnimation from './WaitingAnimation';
export default {
data() {
return {
Uid: null,
}
},
computed: {
CurrentComponent() {
return this.Uid == null ? WaitingAnimation : App;
}
}
beforeMount() {
//While Firebase is initializing `Firebase.auth().currentUser` will be null
let currentUser = Firebase.auth().currentUser;
//Check currentUser in case you previously initialize Firebase somewhere else
if (currentUser) {
//if currentUser is ready we just finish here
this.Uid = currentUser.uid;
} else {
// if currentUser isn't ready we need to listen for changes
// onAuthStateChanged takes a functions as callback and also return a function
// to stop listening for changes
let authListenerUnsuscribe = Firebase.auth().onAuthStateChanged(user => {
//onAuthStateChanged can pass null when logout
if (user) {
this.Uid = user.uid;
authListenerUnsuscribe(); //Stop listening for changes
}
});
}
}
}
</script>

Firebase profile integrations

What I am trying to do here is to implement a functionality on the start-up. I want my user's firebase authentication email variable to set a variable that represents the current user logged into my app?
With the following code the line that sets the user variable works after I click log in but not on page load! The console logs work perfectly on start-up but not the setting of user to the email...
crossfitApp.controller('globalIdCtrl', ["$scope", 'defautProfileData',
function ($scope, defautProfileData) {
var dataRef = new Firebase("https://glowing-fire-5401.firebaseIO.com");
//defautProfileData.country;
$scope.authenticated = {
currentUser: 10007,
emailAddress: "",
settings: "",
};
$scope.auth = new FirebaseSimpleLogin(dataRef, function (error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
//Not working
$scope.authenticated.currentUser = user.id;
console.log('User ID: ' + user.id + ', ProvideFr: ' + user.provider + user);
console.log(user);
} else {
console.log($scope.auth);
alert('deuces');
//!Trigger not logged in
}
});
}
]); //GlobaldCtrl
The callback to FirebaseSimpleLogin is not invoked inside the scope of Angular's HTML compiler. Normally, whenever you invoke ng-click, ng-submit, et al, Angular fires $scope.$apply(), which checks for any changes to the bound JavaScript variables and applies those to the DOM elements.
When an event outside of Angular changes a variable, you need to let Angular know by manually triggering a $apply event. The safest way to accomplish this is to use $timeout:
angular.controller('MyCtrl', function($scope, $timeout) {
$scope.auth = new FirebaseSimpleLogin(dataRef, function (error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
$timeout(function() {
$scope.currentUser = user.uid;
});
} else {
console.log('not logged in');
}
});
In general, prefer user.uid to user.id, as it is unique across providers.
A library like AngularFire can save you a lot of trouble, as it abstracts a lot of the complexities of integrating Firebase and Angular.

Resources