161

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?

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Jophin Joseph
  • 2,864
  • 4
  • 27
  • 40

14 Answers14

183

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.
  }
});
bojeil
  • 29,642
  • 4
  • 69
  • 76
  • 9
    This is inconsistent for me. http://stackoverflow.com/questions/39395158/onauthstatechanged-inconsistent#39395158 – Dan P. Sep 08 '16 at 16:03
  • 3
    Using the onAuthStateChanged, is there a way to tell if a user is new? – BB Developer Nov 06 '17 at 23:49
  • 1
    Yes, but not through `onAuthStateChanged`. This was added in 4.6.0: https://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 07 '17 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. – thevengefulco Nov 11 '17 at 00: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 '17 at 17:46
  • It works as it was made to do, but maybe not in the way that someone looking for the answer intends for it to. – thevengefulco Nov 21 '17 at 22:00
  • @tear728 You can schedule auth popup on tab VisibilityChange Event. [link](https://www.html5rocks.com/en/tutorials/pagevisibility/intro/) – Qwerty Aug 15 '18 at 07:28
113

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.
}
Daniel Passos
  • 1,307
  • 1
  • 9
  • 8
  • 17
    This is returning null for me even after an immediate login. – Aarmora Feb 04 '17 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. – Daniel Passos Feb 07 '17 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. – cheshireoctopus Oct 01 '17 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 '18 at 12:13
  • If you are getting null, it's probably because it hasn't initialized yet. – Arnaldo Capo Sep 17 '18 at 19:55
  • For the Flutter version of `firebase_auth` plugin, I'm using the async version `await _firebaseAuth.currentUser()` and it works great. I print the result before and after logging out, and I obtain the user data and `null` respectively. Thanks. – Timbergus Jan 09 '19 at 00:19
  • This returns signed in even if the user is deleted – TSR Feb 26 '19 at 15:45
49

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}}}
    // />
Qwerty
  • 29,062
  • 22
  • 108
  • 136
  • 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 '19 at 04: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 '19 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 '19 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 03 '19 at 12:19
  • I was stuck on this for a bit... this method works pretty well. Thank you. – spetz83 Jul 29 '20 at 04:08
29

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

Daniel Vukasovich
  • 1,692
  • 1
  • 18
  • 26
  • 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 '19 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! :)

ravish.hacker
  • 1,189
  • 14
  • 21
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
  }
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Ben Winding
  • 10,208
  • 4
  • 80
  • 67
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.

maudulus
  • 10,627
  • 10
  • 78
  • 117
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()

Jonathan
  • 3,893
  • 5
  • 46
  • 77
  • 1) is a great answer! My plain js solution: ```const user = await new Promise((resolve, reject) => auth.onAuthStateChanged(resolve, reject))``` – Andreas Jan 07 '22 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

muetzerich
  • 5,600
  • 7
  • 37
  • 52
1

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.
  }
})
Ahmet Şimşek
  • 1,391
  • 1
  • 14
  • 24
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

Jmndao
  • 1
  • 3
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
    }

}
Luke Vo
  • 17,859
  • 21
  • 105
  • 181
-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
    }
Genes
  • 300
  • 1
  • 3
  • 16
Daniel
  • 7