Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 - Firebase maintaining logged in state on page refresh

So I'm really close to having this figured out. Basically, I have login working and I have the code setup to watch for onAuthStateChanged, but the problem is that when I refresh the page, even if the user is currently logged in, it still redirects to the /login page because the auth guard that I have setup, checks if the user is logged in.

I have an auth service setup that does all the authentication checks.

In my auth service constructor, that's where I have my onAuthStateChanged code setup:

constructor(
    private auth: AngularFireAuth,
    private router:Router,
    private db:AngularFireDatabase,
  ) {

    firebase.auth().onAuthStateChanged(function(user) {
     if (user) {
       // What do I need to put here to ensure that the auth guard goes to the intended route?
     }
    });

  }

Here is my auth guard's canActivate() method:

 canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean> {

      if(this.authService.getAuthenticated()) {
        return Observable.of(true);
      } else {
        this.router.navigate(['/login']);
      }
    }

And here is the getAuthenticated() method:

getAuthenticated() {
    return firebase.auth().currentUser;
  }

Update:

Here are the methods that are called when the user actually logs in. In the actual component, here is the login method:

login(data, isValid) {
    if(isValid) {

      this.authService.login(data.email, data.password)
          .then(
              (data) => {
                console.log('data', data);
                this.router.navigate(['/projects'])
              }, error => {
                this._utilities.createToasty({
                  msg: "The email/password combination is incorrect.",
                  type: "error"
                });
              }
          );
    }
  }

And in the auth service, here's the login method:

login(email, password) {
    return firebase.auth().signInWithEmailAndPassword(email, password);
  }

So right now, I'm not entirely sure what code I need to put where to ensure that when my user refreshes the page, the auth guard picks up on that and actually allows the user to go to the route instead of always redirecting to the /login route.

Thoughts?

like image 886
Stevie Star Avatar asked May 21 '17 15:05

Stevie Star


2 Answers

I don't really know the answer since I don't use firebase so those are more pointers

Guess 1: the user is in an intermediate state

The function that runs the authentication in the background hasn't run yet when the canActivate method does. Thus the current user isn't set yet. I guess firebase simply goes to your local storage and check if the token is valid. If it's what it does then simply make sure that this happens before canActivate.

However it could run before and have the current user still undefined if it's asynchronous which is quite possible. Then you'll have to make sure the canActivate runs after it resolves.

Actually, better yet: use the observable in your can activate method as seen:

  • here

  • here

code:

 canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.af.auth.map((auth) => {
        if (!auth) {
          this.router.navigateByUrl('login');
          return false;
        }
        return true;
    }).take(1);
  }

Guess 2: You have to store something in local storage.

Those authentication services usually requires to store a token in localstorage, this token is then used to authenticate the user upon page refresh. Of what I can read this is done in the background with firebase. You could still check the local storage/ session storage just to be sure you see info's there.

If you don't see anything relevant in localStorage/sessionStorage, chances are you have to put in there something upon login.

like image 147
Ced Avatar answered Nov 11 '22 22:11

Ced


I had the same problem as yours, i did the following to solve it with localStorage:

login(email, password): Observable<AuthInfo> {
    return this.fromFirebaseAuthPromise(this.afAuth.auth.signInWithEmailAndPassword(email, password));
}

I just added localStorage.setItem("user", this.afAuth.auth.currentUser.uid); to the login response and localStorage.removeItem("user"); to the logout method.

fromFirebaseAuthPromise(promise): Observable<any> {

const subject = new Subject<any>();

promise
  .then(res => {
      const authInfo = new AuthInfo(this.afAuth.auth.currentUser.uid);
      this.authInfo$.next(authInfo);
      subject.next(res);
      subject.complete();
      localStorage.setItem("user", this.afAuth.auth.currentUser.uid);
    },
    err => {
      this.authInfo$.error(err);
      subject.error(err);
      subject.complete();
    });

    return subject.asObservable();
}



logout() {
    this.afAuth.auth.signOut();
    localStorage.removeItem("user");
    this.authInfo$.next(AuthService.UNKNOWN_USER);
    this.router.navigate(['/login']);
}

and changed the canActivate() method to the following:

canActivate() {
        //you can check token is exists or not here
        if (localStorage.getItem('user')) {
            console.log("user ist logged in ....", localStorage.getItem('user'));

            return true;
        } else {
            console.log("user is not logged in");

            this.router.navigate(['/login']);
            return false;
        }
    }
like image 3
iFadi Avatar answered Nov 11 '22 22:11

iFadi