firebase-js-sdk
firebase-js-sdk copied to clipboard
FR: please add function Auth.getCurrentUser() as a promise that will return current user or null
- Firebase SDK version: 4.9.0
- Firebase Product: auth
Auth.currentUser
object is unreliable to get user from persistence storage. If the authentication system could be pending and don't really have ensure finishing endpoint then you should have a promise for it
So I want to have promise function Auth.getCurrentUser()
in addition to onAuthStateChange
that would eventually return user but would return null if no user exist in persistence storage or user require sign in from expired token
Hmmm this issue does not seem to follow the issue template. Make sure you provide all the required information.
You can easily implement that on your own with a couple of lines:
function getCurrentUser(auth) {
return new Promise((resolve, reject) => {
const unsubscribe = auth.onAuthStateChanged(user => {
unsubscribe();
resolve(user);
}, reject);
});
}
@bojeil-google If the user does not signed in will that promise would ever fired for null?
And I think this function is really common enough to included in the SDK
Yes that promise will fire. onAuthStateChanged
always triggers with the initial state even when it is null. That is what you care for here, the current state. After that is determined, you unsubscribe.
I see but anyway if it really just a couple line and will be the same for everyone so why it not be inside SDK. Did you not consider it useful?
And also that function would fire only one time at first call and will not fire again in second call am I right? What I want from getCurrentUser()
function is consistently return null if it already checking in persistence storage, or return the actual current user after that
At least it should have a function like getPreparedAuthStated()
or isPreparedAuthFinished
I have no idea what you mean by: " consistently return null if it already checking in persistence storage, or return the actual current user after that".
@bojeil-google If I don't misunderstand, the onAuthStateChanged in promise
function you present will work only on the first call. If I call that function on the second time it will never return because the auth state never change again after that
This is not what I want from getCurrentUser()
. I want this function to actively checking that user is not really signed in before. I want it to be promise just in case that firebase auth does not finish initializing yet, in that case the user was signed in before but firebase itself are not ready. So return promise is better solution
What I want is
- If called when firebase auth preparation is not finished : return promise to wait
- after preparation finished but user was not signed in before : resolve null from the promise
- after preparation finished and user was signed in before : resolve currentUser from the promise
- If called after firebase auth preparation is finished
- but user is not signed in : immediately resolve null from the promise
- and user is signed in : immediately resolve currentUser from the promise
I want this consistence behavior
for getCurrentUser()
function
But if I don't misunderstand, the function you present will never resolve if that function was called after firebase auth preparation is finished unless the user try to signed in
If I misunderstand above, which means onAuthStateChanged
will always fire once whenever it get called for the current user without the need of signIn
or signOut
action. Then I would like to request that this behavior should be mentioned in the document
Please read the documentation, you presume some incorrect assumptions.
If we were to implement a promise that returns the current user, it will be done as I described.
onAuthStateChanged
will always trigger every time it is set. It is super easy to test that.
If it is called while Auth is still initializing and loading state, it will wait for it to complete before resolving. If you call it after, it will still trigger immediately with the current user.
If you want some custom function or listener that triggers based on your own custom requirements, we provide all the APIs you need to build that.
If you are experiencing issues with the behavior of onAuthStateChanged
listener, please file the bugs and we will make sure to address them.
@bojeil-google I know that if I call onAuthStateChanged
before initializing and loading state it would be triggered. However I read the document and haven't seen mentioning about what happen if I register onAuthStateChanged
after it finished initializing and loading state but the user is not signed in yet
I know that it would be surely be triggered whenever user would sign in by any means. But if user never sign in at all would it trigger that it change from null
to null
?
Why is that?
Is it changed from null as auth not initialized
to null as user not signed in
?
And if I register onAuthStateChanged
after finished initialized why it still trigger when user AuthState
was not changed at all?
In the document
https://firebase.google.com/docs/reference/js/firebase.auth.Auth
I have seen the explanation only these
Adds an observer for changes to the user's sign-in state.
Prior to 4.0.0, this triggered the observer when users were signed in, signed out, or when the user's ID token changed in situations such as token expiry or password change. After 4.0.0, the observer is only triggered on sign-in or sign-out.
At this line
the observer is only triggered on sign-in or sign-out
is very pinpoint that if user not doing any signin/signout at all it should not be triggered just from registering the observer
Also in here is not mention about that too
https://firebase.google.com/docs/auth/web/manage-users
What document you think I should read?
@bojeil-google Do you have a link to the document you mention about this behavior?
This is the reference for onAuthStateChanged
:
https://firebase.google.com/docs/reference/js/firebase.auth.Auth#onAuthStateChanged
This is the guide on how to use it: https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed_in_user
Any time you call this listener after initial state has been determined, it will trigger with the current state (null if no user signed in, and with a user if a user is signed in). If you don't believe, then just try it out yourself.
@bojeil-google It not that I would not belief but it the API name itself is misleading so I got confused. I test it and it seem the real behavior is like you stated. But because it not mentioned in the document I don't know I should rely on it or not
That's why I said if I misunderstand then it should be documented about this behavior. Because the API name is "onAuthStateChanged" but it will also firing one time when the auth state not really changed
Fair enough. We should update the documentation and references to mention that. Thanks for pointing that out.
I will 👍 this request, I just wasted a lot of time trying to find a method on the api to load the currently logged in user on page load before I found this issue. It needs to be mentioned that this will also load the user from the persistence if it is persisted.
Here is modified version of @bojeil-google function that can be used anytime to get a promise that resolves into current user or null.
let userLoaded = false;
function getCurrentUser(auth) {
return new Promise((resolve, reject) => {
if (userLoaded) {
resolve(firebase.auth().currentUser);
}
const unsubscribe = auth.onAuthStateChanged(user => {
userLoaded = true;
unsubscribe();
resolve(user);
}, reject);
});
}
I've 👍 this request, too. As the Thaina, I also think the documentation about onAuthStateChanged
is not clear about the discussed behavior, and even the API name itself is misleading. To me, it seems that an action from the user is needed in order to trigger the function, very much like the HTML onClick
event.
@atishpatel I've tried that but it deosnt work for me :/
onAuthStateChanged
is called when the user auth state changes and when the firebase.auth
is first initialized.
Does anyone know if firebase.auth().currentUser
caches the user, with or without persistence enabled. Or if requires a new network request API call everytime?
firebase.auth().currentUser
caches the user regardless of persistence.
No idea... I'm using nuxt and have this as a pluggin:
// PLUGIN FILE
import firebaseConfig from '~/firebase'
import firebase from 'firebase/app'
if (!firebaseConfig) {
throw new Error('missing firebase configuration file')
}
export default ({store, redirect}) => {
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig)
}
return firebase.auth().onAuthStateChanged((user) => {
if (user) {
store.commit('firebase/setUser', user)
}
})
}
// VUEX STORE MODULE
...
setUser(state, user) {
state.user = user
return this.dispatch('firebase/setProfileRef', state.user.uid)
}
setProfileRef: firestoreAction(({ bindFirestoreRef }, uid) => {
return bindFirestoreRef('profile', firebase.firestore().collection('profiles').doc(uid))
}),
...
sometimes causes inf recursion 🙃
bump. I'm finding many hours wasted on the auth state. I vote for a simple API the consistently gives us the user or null when we need it.
See, authStateChanged has a purpose, and therefore it can be used to workaround the fact that .auth().currentUser is not a promise and it's value is not trustable (because it depends on asynchronous tasks), it's purpose is to watch changes at the auth state. getCurrentUser, method that doesn't exists currently, has a different purpose: to simply get the current user despite how many changes happened at the application until now.
Dont take this wrong, please, but having methods implicates in having closed scopes of use, it's a basic pattern rule.
Either making it possible to use async .auth.currentUser or .auth().getCurrentUser().then would be desired implementation.
@zerobytes I just wrap it in a promise before calling it from other services.
function getUser() {
return new Promise((resolve, reject) => {
let user = this.firebase.auth().currentUser;
return user ? resolve(user) : reject(new Error('No user signed in'));
}
Hey @holmberd, I understood what you did, but it has no point in having a promise if your action is not asynchronous, What we intent to do with getCurrentUser() is having a promise which will await until firebase auth has actually finished the process of identifying the current user. This task happens asynchronously which is the reason why an already authenticated app can fail get firebase.auth().currentUser, as firebase auth has not yet finished the process of setting it.
@atishpatel Answer is more like what we need, and will do the job, but it's still a workaround. Firebase SDK should have it as a promise.
What we intent to do with getCurrentUser() is having a promise which will await until firebase auth has actually finished the process of identifying the current user. This task happens asynchronously which is the reason why an already authenticated app can fail get firebase.auth().currentUser, as firebase auth has not yet finished the process of setting it.
I haven't seen any such failures occur and therefore can't confirm. But it does look like there is a fair share of ambiguity in regards to the API usage in general.
As I see it getCurrentUser
is only guaranteed to be set after a firebase.auth().onAuthStateChanged
event when a user has signed in, or in the resolved promise of firebase.auth().signIn*(provider)
. Where the latter is more useful if you are checking if the signed in user is a new user additionalUserInfo.isNewUser
.
For me onAuthStateChanged
gate-keeps any services relying on the signed in user state and maintain the asynchronous flow.
@holmberd I guess you might not know but, firebase library has an initialize duration in the loading time that, even user have been logged in and even have persistent logged in data stored in the local session, the currentUser
would still be null
until firebase system finish initialized itself
And this issue was voicing that, we don't really want a long term listener for the gate-keeps
functionality you talk about. We want just a Promise
or a Thenable
to be that kind of gate-keep
in the async function. We just want to call await getCurrentUser()
and if it is null
we then just show the logged-in button or launch the redirect flow. But we just need to call it one time and don't want to have it keep listening. So why we need to have it as listener that we need to unsubscribe by ourselves? It make our code unnecessary more bloated
@Thaina I have not experienced that issue. I initialize firebase before initializing any services depending on it. Could you show me an example of such behaviour to test?
@holmberd You can just read from the doc
https://firebase.google.com/docs/auth/web/manage-users
The recommended way to get the current user is by setting an observer on the Auth object:
By using an observer, you ensure that the Auth object isn't in an intermediate state—such as initialization—when you get the current user. When you use signInWithRedirect, the onAuthStateChanged observer waits until getRedirectResult resolves before triggering.
It's always mentioned since the beginning
ensure that the Auth object isn't in an intermediate state—such as initialization