Angular 5 canActivate redirecting to login on browser refresh

后端 未结 1 1851
南笙
南笙 2021-02-03 10:15

Angular 5 authentication app using angularfire2 and firebase. The app works fine navigating using in-app links e.g. redirect to dashboard after login or link to another page (co

相关标签:
1条回答
  • 2021-02-03 10:56

    The canActivate() method is called directly on page refresh. So it always returns false:

    canActivate() {
      this.authService.authState.subscribe(state => {
        this.status = state.toString(); // This is called async/delayed.
      });
      // so method execution proceeds
    
      // isLoggedIn() returns false since the login stuff in AuthService.constructor
      // is also async:    .subscribe((user) => { /* delayed login */ });
      if(this.authService.isLoggedIn()) {
        return true;
      }
    
      // so it comes here
      this.router.navigate(['/']); // navigating to LoginComponent
      return false;                // and canActivate returns false
    }
    

    The solution:

    import { CanActivate, Router, ActivatedRouteSnapshot,
             RouterStateSnapshot } from '@angular/router';
    
    // ...
    
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
      // when the user is logged in and just navigated to another route...
      if (this.authService.isLoggedIn) { return true; } 
    
      // proceeds if not loggedIn or F5/page refresh 
    
      // Store the attempted URL for redirecting later
      this.authService.redirectUrl = state.url;
    
      // go login page
      this.router.navigate(['/']);
      return false;
    }
    

    now, back in the little changed AuthService: (only have changed/relevant code here)

    export class AuthService {
    
      // new
      redirectUrl: string;
    
      // BehaviorSubjects have an initial value.
      // isLoggedIn is property (not function) now:
      isLoggedIn = new BehaviorSubject<boolean>(false);
    
      // params declared private and public in constructor become properties of the class
      constructor(private firebaseAuth: AngularFireAuth, private router: Router) {
        // so this.user is not required since it is reference to this.firebaseAuth
        this.firebaseAuth.authState.subscribe((user) => {
          if (user) {
            this.loggedIn.next(true);
    
            // NOW, when the callback from firebase came, and user is logged in,
            // we can navigate to the attempted URL (if exists)
            if(this.redirectUrl) {
              this.router.navigate([this.redirectUrl]);
            }
          } else {
            this.loggedIn.next(false);
          }
        }
      }
    
    }
    

    Note: I have written this code in the answer box and compiled it in my brain. So bugs may exist. Also I don't know if this is actually best practise. But the idea should be clear?!

    Based on the Angular Routing Guide

    Seems like there are similar problems/solutions out there: Angular 2 AuthGuard + Firebase Auth

    0 讨论(0)
提交回复
热议问题