165

I'm using the firebase node api in my javascript files for Google login.

firebase.initializeApp(config);
let provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider);

This works fine and the user is able to login with his Google credentials. When the user visits the page again, the popup opens again but since he has already logged in, the popup closes without requiring any interaction from the user. Is there any way to check if there is already a logged in user before prompting the popup?

15 Answers 15

191

https://firebase.google.com/docs/auth/web/manage-users

You have to add an auth state change observer.

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    // User is signed in.
  } else {
    // No user is signed in.
  }
});
7
  • 9
    This is inconsistent for me. stackoverflow.com/questions/39395158/…
    – Dan P.
    Sep 8, 2016 at 16:03
  • 3
    Using the onAuthStateChanged, is there a way to tell if a user is new? Nov 6, 2017 at 23:49
  • 1
    Yes, but not through onAuthStateChanged. This was added in 4.6.0: firebase.google.com/support/release-notes/js#4.6.0 You can get it from the sign-in method or from currentUser.metadata (last sign in time and creation time)...
    – bojeil
    Nov 7, 2017 at 19:34
  • 1
    @bojeil I think there is a caveat to this method. You can put this code on one page, and it will trigger even when you are not on that page. It triggers when the AuthState changes. If it changes on other pages you will effectively have it trigger when you don't want it to. At least, that's what is happening to me right now. Nov 11, 2017 at 0:07
  • 1
    This will detect sign in events on other tabs. It works as intended. Firebase Auth propagates sign in events across windows.
    – bojeil
    Nov 12, 2017 at 17:46
118

You can also check if there is a currentUser

var user = firebase.auth().currentUser;

if (user) {
  // User is signed in.
} else {
  // No user is signed in.
}
8
  • 19
    This is returning null for me even after an immediate login.
    – Aarmora
    Feb 4, 2017 at 15:01
  • 11
    That's strange, it works for me, maybe the problem is because auth is async and _ currentUser_ was not updated yet. Feb 7, 2017 at 11:02
  • 3
    From the docs: currentUser might also be null because the auth object has not finished initializing. If you use an observer to keep track of the user's sign-in status, you don't need to handle this case. Oct 1, 2017 at 23:38
  • You can't rely on it. See official docs: "There are some cases where getCurrentUser will return a non-null..."
    – Andrew
    Jun 13, 2018 at 12:13
  • If you are getting null, it's probably because it hasn't initialized yet. Sep 17, 2018 at 19:55
50

It is not possible to tell whether a user will be signed when a page starts loading, there is a work around though.

You can memorize last auth state to localStorage to persist it between sessions and between tabs.

Then, when page starts loading, you can optimistically assume the user will be re-signed in automatically and postpone the dialog until you can be sure (ie after onAuthStateChanged fires). Otherwise, if the localStorage key is empty, you can show the dialog right away.

The firebase onAuthStateChanged event will fire roughly 2 seconds after a page load.

// User signed out in previous session, show dialog immediately because there will be no auto-login
if (!localStorage.getItem('myPage.expectSignIn')) showDialog() // or redirect to sign-in page

firebase.auth().onAuthStateChanged(user => {
  if (user) {
    // User just signed in, we should not display dialog next time because of firebase auto-login
    localStorage.setItem('myPage.expectSignIn', '1')
  } else {
    // User just signed-out or auto-login failed, we will show sign-in form immediately the next time he loads the page
    localStorage.removeItem('myPage.expectSignIn')

    // Here implement logic to trigger the login dialog or redirect to sign-in page, if necessary. Don't redirect if dialog is already visible.
    // e.g. showDialog()
  }
})



I am using this with React and react-router. I put the code above into componentDidMount of my App root component. There, in the render, I have some PrivateRoutes

<Router>
  <Switch>
    <PrivateRoute
      exact path={routes.DASHBOARD}
      component={pages.Dashboard}
    />
...

And this is how my PrivateRoute is implemented:

export default function PrivateRoute(props) {
  return firebase.auth().currentUser != null
    ? <Route {...props}/>
    : localStorage.getItem('myPage.expectSignIn')
      // if user is expected to sign in automatically, display Spinner, otherwise redirect to login page.
      ? <Spinner centered size={400}/>
      : (
        <>
          Redirecting to sign in page.
          { location.replace(`/login?from=${props.path}`) }
        </>
      )
}

    // Using router Redirect instead of location.replace
    // <Redirect
    //   from={props.path}
    //   to={{pathname: routes.SIGN_IN, state: {from: props.path}}}
    // />
5
  • Do you think you could help me with this? I tried the auto-login redirect you have at the top of your answer, and now I can't seem to remove it. I've cleared out my local storage entirely and I still can't access my firebase site logged out because of the constant redirects.
    – hego64
    Nov 28, 2019 at 4:50
  • @hego64 Have you actually logged out of your firebase app? This solution doesn't do the logging in. It only allows to skip sign-in form if user didn't sign out in previous session. / EDIT: Are you in a redirect loop? I will update the answer.
    – Qwerty
    Nov 28, 2019 at 14:01
  • Yes, I should have been more clear, I was stuck in a redirect loop. I'd log out, and instead of going back to the app, I was taken straight back to the sign-in page. I was able to fix it by rolling back to a previous deploy, but aside from that I wasn't sure what to do to fix the issue.
    – hego64
    Nov 29, 2019 at 19:38
  • 1
    @hego64 Users that are not signed-in shouldn't be able to walk around freely anwyay, so redirecting to log-in page is correct, but if there are routes that are available without authentication, you then must move the logic to the router or particular routes instead, much like in my PrivateRoute wrapper example. So if user is on a restricted page and signs out, then they will be redirected to log-in page. Your other routes will not have this logic implemented.
    – Qwerty
    Dec 3, 2019 at 12:19
  • I was stuck on this for a bit... this method works pretty well. Thank you.
    – spetz83
    Jul 29, 2020 at 4:08
30

There's no need to use onAuthStateChanged() function in this scenario.

You can easily detect if the user is logged or not by executing:

var user = firebase.auth().currentUser;

For those who face the "returning null" issue, it's just because you are not waiting for the firebase call to complete.

Let's suppose you perform the login action on Page A and then you invoke Page B, on Page B you can call the following JS code to test the expected behavior:

  var config = {
    apiKey: "....",
    authDomain: "...",
    databaseURL: "...",
    projectId: "..",
    storageBucket: "..",
    messagingSenderId: ".."
  };
  firebase.initializeApp(config);

    $( document ).ready(function() {
        console.log( "testing.." );
        var user = firebase.auth().currentUser;
        console.log(user);
    });

If the user is logged then "var user" will contain the expected JSON payload, if not, then it will be just "null"

And that's all you need.

Regards

1
  • 4
    @Mauricio Silvestre is it? Using firebase.auth.currentUser returns undefined whether or not I am signed in. Only auth() returns the correct result when signed in.
    – tsujp
    Feb 24, 2019 at 10:33
9

One another way is to use the same thing what firebase uses.

For example when user logs in, firebase stores below details in local storage. When user comes back to the page, firebase uses the same method to identify if user should be logged in automatically.

enter image description here

ATTN: As this is neither listed or recommended by firebase. You can call this method un-official way of doing this. Which means later if firebase changes their inner working, this method may not work. Or in short. Use at your own risk! :)

9

This works:

async function IsLoggedIn(): Promise<boolean> {
  try {
    await new Promise((resolve, reject) =>
      app.auth().onAuthStateChanged(
        user => {
          if (user) {
            // User is signed in.
            resolve(user)
          } else {
            // No user is signed in.
            reject('no user logged in')
          }
        },
        // Prevent console error
        error => reject(error)
      )
    )
    return true
  } catch (error) {
    return false
  }
}
5

usage example with latest library version

import { initializeApp } from "firebase/app";
import { getAuth, onAuthStateChanged } from "firebase/auth";

const firebaseConfig = {
  ...
};

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);

onAuthStateChanged(auth, user => {
  if (user) {
    // User is signed in.
  } else {
    // No user is signed in.
  }
})
5

I use react and react-router-dom with loader function, after refreshing page my loader can't load data from firestore because firebase app need a litle time to initialize. I find easy solution. I set auth method before checking is user logged in and it's works. Again, you need to wait firebase app to settle and this auth method do just that it wait app to initialize. Here my solution, it's so simple:

    export const getContacts = async () => {
        await auth.authStateReady()
        const currentUser = auth.currentUser

        if (!currentUser) return false
       // rest of code ...
    }

await auth.authStateReady() is promise and it will wait to resolve with user or not (if user isn't logged in). So simple, it took me 6 hours to find this method.

2
  • This is the way to go!
    – Sebastian
    Mar 1 at 7:58
  • Fantastic, so simple but so hidden in any Firebase documentation! Finally no more currentUser == null
    – matsbauer
    2 days ago
3

If you are allowing anonymous users as well as those logged in with email you can use firebase.auth().currentUser.isAnonymous, which will return either true or false.

2

There are technically 3 possibilities with promises:

// 1) best option, as it waits on user...

const isLoggedIn: any = await new Promise((resolve: any, reject: any) =>
this.afa.onAuthStateChanged((user: any) =>
  resolve(user), (e: any) => reject(e)));

console.log(isLoggedIn);

// 2) may experience logging out state problems...

const isLoggedIn2 = await this.afa.authState.pipe(first()).toPromise();

console.log(isLoggedIn2);

// 3) technically has a 3rd option 'unknown' before user is loaded...

const isLoggedIn3 = await this.afa.currentUser;

console.log(isLoggedIn3);


// then do something like (depending on your needs) with 1, 2, or 3:

if (!!isLoggedIn) {
  // logged in
}

Also note, the example is angular, but you can replace this.afa with firebase.auth()

1
  • 1) is a great answer! My plain js solution: const user = await new Promise((resolve, reject) => auth.onAuthStateChanged(resolve, reject))
    – Andreas
    Jan 7, 2022 at 23:55
1

use Firebase.getAuth(). It returns the current state of the Firebase client. Otherwise the return value is nullHere are the docs: https://www.firebase.com/docs/web/api/firebase/getauth.html

1
  • 24
    Deprecated, avoid firebase.getAuth()
    – Mazze
    Aug 1, 2016 at 12:13
0

For the firebase v9.8.1 and angular v13.3.5, I'm not using AngularFire, then I used this service that works well.

@Injectable({providedIn: 'root'})
export class IsAuthenticatedGuard implements CanActivate {

  canActivate(): Promise<boolean> {
    return new Promise(resolve =>
      onAuthStateChanged(getAuth(),
        user => resolve(!!user),
        _ => resolve(false)));
  }

}

0

You can as well use a use-effect hook in a context to check if the user is authenticated per page (when the user navigates to a page). However, make sure to not include the auth page.

  React.useEffect(() => {
    if (router.asPath !== "/auth") {
      onAuthStateChanged(auth, (user) => {
        if (!user) {
          window.location.href = "/auth";
        }
      });
    }
  }, [router]);

Here I was using nextJS but it's pretty much the same with just reactJS

0

I was skeptical of the other answer because the name onAuthStateChanged confuses me. I assumed it only work if a user has logged in. This is not the case, when the user is new, onAuthStateChanged is still called (with null as its parameter, NOT undefined).

This is a Promise-based code to get a user if they logged in before:

class AccountService {

    #auth: any;
    #initUserLoaded = false;
    #userCallback?: (user: any) => void;

    constructor() {
        // Other code to initialize Firebase

        fbAuth.onAuthStateChanged(
            this.#auth,
            (user: FbUser) => void this.#onUserChanged(user));
    }

    #onUserChanged(user: any) {
        this.#initUserLoaded = true;

        const cb = this.#userCallback;
        if (cb) {
            this.#userCallback = undefined;
            cb(user);            
        }
    }

    async #getCurrentUserAsync(): Promise<any | undefined> {
        let result = this.#auth.currentUser;
        if (result || this.#initUserLoaded) { return result; }

        result = await new Promise<any>(r => {
            this.#userCallback = r;
        }),
        this.#userCallback = undefined;

        return result ?? undefined; // Convert null to undefined
    }

}
-9

First import the following

import Firebase
import FirebaseAuth

Then

    // Check if logged in
    if (Auth.auth().currentUser != null) {
      // User is logged in   
    }else{
      // User is not logged in
    }
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.