20
votes

I'm a little confused by some behaviour I'm seeing with Firebase. I never used the old version, but I believe getRedirectResult is new since they joined forces with Google.

I have a SPA that I am using Vue.js with vue-router, and Firebase for. There is a landing page, and then another view for which users can be logged in, or not. Login is done by redirect. When this second view is loaded, I check getRedirectResult in the vue-router 'activate' hook, and if there is a user, do some other stuff with the user information.

The problem proceeds thusly:

  1. We are on second page. User logs in. getRedirectResult is called and finds a user. Yay.
  2. User logs out. We are back on landing page.
  3. We click a button that leads us to second page. getRedirectResult is called and finds the previous user. What?! No!

I can't find anything on whether I am missing something and need some kind of extra check in place, or to somehow forcibly refresh the page after logout so it forgets that the last user logged in, or if this would be considered a bug. Any assistance would be greatly appreciated!

getRedirectResult call on second page in vue component router 'activate' hook:

firebase.auth().getRedirectResult()
    .then((result) => {
        return result.user;
    }).then((user) => {
        // Do stuff.
    });

Update: Solved by doing a hard page refresh in the logout callback, as follows:

firebase.auth().signOut()
    .then(() => {window.location.href = '/'});
2
Hi, Have you found a solution to this problem other than the hard refresh? - user482594
@user482594 nope; I stopped working on it once I found that fix. - hboo
I contacted google firebase support team about this (case: 0-7351000018196), and they said this is an expected behavior.. wtf? According to them, to log users out from the app who initially signed in with Facebook, the app will need to use Facebook SDK to do so. That is so not firebase! - user482594
Hi, is hard page refresh still working? I'm trying here but it always redirects to /login - madsongr

2 Answers

1
votes

This drove me crazy. As you'll see here (https://github.com/firebase/firebase-js-sdk/issues/133), this is unfortunately intended behavior.

My approach was to avoid using getRedirectResult() entirely and achieve the same functionality by testing for the presence of an authenticated Firebase user (rather than waiting for the redirect callback). In Angular, you use AngularFire's authState observable. With this approach, when you signOut(), there's no issue with that lingering user in your client memory because it wasn't stored in getRedirectResult().

The concept is that you place a Route Guard on the login page. The Route Guard only lets you on to the login page if an authenticated user isn't present. At that point, you log in, and once that succeeds (signInWithRedirect() always takes a few seconds), the firebase user data is loaded into the client, which triggers the Route Guard to block you from the login page and instead redirect you to the location of your choice.

For bonus points, if you want to preserve the returnUrl, store that string in local storage before firing signInWithRedirect(), and then retrieve it in the Route Guard when the redirect happens (and delete it from local storage).

Inspiration from this Firebase blog post: https://firebase.googleblog.com/2018/03/cleanse-your-angular-components.html. Hopefully this can be applied conceptually to what you are doing in Vue.js.

If you're curious, here's what that Route Guard looks like in Angular 2:

import { Injectable } from '@angular/core';
import { AuthService } from './auth.service';
import { Router, CanActivate } from '@angular/router';
import { map } from 'rxjs/operators';

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

  constructor(
    private auth: AuthService,
    private router: Router
  ) { }

  canActivate() {
    return this.auth.firebaseUser$.
      pipe(map(user => {
        // The firebaseuser determines if user is logged in
        // If logged in, block the route with a redirect
        if (user) {
          console.log('User detected', user);
          const returnUrl = localStorage.getItem('returnUrl');
          // Route user to returnUrl, if none, go to profile
          if (returnUrl && returnUrl !== '/') {
            this.router.navigate([returnUrl]);
          } else {
            this.router.navigate(['profile']);
          }
          return false;
        }
        return true;
      }));
  }
}
0
votes

Use a flag to detect if getRedirectResult() has been processed. i.e.

firebase.auth().getRedirectResult()
  .then((result) => {
    return result.user;
  }).then((user) => {
    if (this.authRedirected)
      return; // Skip

    // Do stuff.

    this.authRedirected = true; // Mark redirected
  });