Search code examples
angularangularfire2auth-guard

Angular: browser refresh handling in AuthGuard


I have been trying to implement AuthGuard properly in my webapp. Currently when I navigate within the app, it works fine. But when I refresh, the authService.loggedIn is processed as false before the AuthService finished executing.

Here is my code:

auth.guard.ts



    import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
    import {Injectable} from '@angular/core';
    import {AuthService} from './auth.service';

    @Injectable()
    export class AuthGuard implements CanActivate {
      constructor(private authService: AuthService, private router: Router) {
      }

      canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        if (state.url === '/login') {
          if (this.authService.loggedIn) {
            this.router.navigate(['/']).then().catch();
          } else {
            return true;
          }
        } else {
          if (this.authService.loggedIn) {
            return true;
          } else {
            this.router.navigate(['/login']).then().catch();
          }
        }
      }
    }

auth.service



    import {Injectable, OnInit} from '@angular/core';
    import {AngularFireAuth} from '@angular/fire/auth';
    import {auth} from 'firebase';
    import {Router} from '@angular/router';
    import {Subject} from 'rxjs';

    @Injectable({
      providedIn: 'root'
    })
    export class AuthService implements OnInit {
      loggedIn = false;

      constructor(public afAuth: AngularFireAuth, private router: Router) {
        this.afAuth.authState.subscribe((user) => {
          if (user) {
            this.loggedIn = true;
          }
        });
      }

      ngOnInit() {
      }

      ...
    }

I research online and they mentioned different approach (e.g. https://gist.github.com/codediodeio/3e28887e5d1ab50755a32c1540cfd121) but could not make it work on my app.

One error I encounter when I try this approach is "ERROR in src/app/auth.guard.ts(20,8): error TS2339: Property 'take' does not exist on type 'Observable'." I use

import {AngularFireAuth} from '@angular/fire/auth';

and not

import { AngularFireAuth } from 'angularfire2/auth';

Any help / suggestion is appreciated.

Thanks everyone.


Solution

  • I was able to make AuthGuard with browser refresh work.

    This link helped a lot: https://gist.github.com/codediodeio/3e28887e5d1ab50755a32c1540cfd121

    I just used pipe() to chain operators and used tap() instead of do() since I'm using newer version of rxjs.

    Here is the code:

    
    
        ...
        import {Observable} from 'rxjs';
    
        import {take, map, tap} from 'rxjs/operators';
    
        @Injectable()
        export class AuthGuard implements CanActivate {
          constructor(private router: Router, private afAuth: AngularFireAuth) {
          }
    
          canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable {
            return this.afAuth.authState
              .pipe(
                take(1),
                map(user => !!user),
                tap(
                  loggedIn => {
                    if (!loggedIn) {
                      this.router.navigate(['/login']);
                    }
                  }
                )
              );
          }
        }
    
    

    Thanks.