Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase getRedirectResult is being called after logout

Tags:

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 = '/'});
like image 557
hboo Avatar asked Aug 22 '16 22:08

hboo


People also ask

What is onAuthStateChanged?

Listening to authentication state The module provides a method called onAuthStateChanged which allows you to subscribe to the users current authentication state, and receive an event whenever that state changes.

What triggers onAuthStateChanged?

onAuthStateChanged. 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.


Video Answer


1 Answers

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;
      }));
  }
}
like image 67
Greg Avatar answered Oct 15 '22 05:10

Greg