Search code examples
angularfirebasefirebase-authenticationangular2-routingangular2-guards

Angular 2 AuthGuard with Firebase Auth redirects each time to SignIn page


I trying to build AuthGuard for Angular 2 application with firebase-auth but there is one issue.

After the application loads If I'm signed in the AuthGuard returns false because of async operation.

The final effect is when page loads the AuthGuard returns firstime false and then true, so eachtime redirects to signIn page and not to the root page.

I thougth I use *ngIf to show only SignInComponent, if the user is not sign in, but this is not the solution because avery time will be SignInComponent visible for short time.

How to solved this Issue not to redirect to signIn page immediately?

Thank You for Your answers.

AuthService

@Injectable()
export class AuthService {

  static UNKNOWN_USER = new AuthInfo(null);
  user: Observable<firebase.User>;
  authInfo$: BehaviorSubject<AuthInfo> = new BehaviorSubject<AuthInfo>(AuthService.UNKNOWN_USER);

  constructor(private afAuth: AngularFireAuth) {
    this.afAuth.authState.subscribe(
      auth => {
        if (auth) {
          const authInfo = new AuthInfo(auth.uid);
          this.authInfo$.next(authInfo);
        } else {
          this.authInfo$.next(AuthService.UNKNOWN_USER);
        }
      },
      err => {
        console.log(err);
      }
    );
  }
}

AuthInfo

export class AuthInfo {
  constructor(
    public $uid: string
  ) {

  }

  isLoggedIn() {
    return !!this.$uid;
  }
}

AuthGuard

@Injectable()
export class AuthGuard implements  CanActivate {

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

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    return this.authService.authInfo$
      .map( authInfo => authInfo.isLoggedIn())
      .take(1)
      .do(allowed => {
        console.log('authguard: ' + allowed);
        if (!allowed) {
           this.router.navigate(['/signin']);
        }
    });
  }
}

Solution

  • Sorry, It was duplicated question for this question

    My solution was

    AuthService

    @Injectable()
    export class AuthService {
    
      static UNKNOWN_USER = new AuthInfo(null);
      authInfo$: BehaviorSubject<AuthInfo> = new BehaviorSubject<AuthInfo>(AuthService.UNKNOWN_USER);
    
      constructor(private afAuth: AngularFireAuth) {}
    
      getAuthInfo(): Observable<AuthInfo> {
        return this.afAuth.authState.map(
          auth => {
            if (auth) {
              const authInfo = new AuthInfo(auth.uid);
              this.authInfo$.next(authInfo);
              return authInfo;
            } else {
              this.authInfo$.next(AuthService.UNKNOWN_USER);
              return AuthService.UNKNOWN_USER;
            }
          },
          err => {
            console.log(err);
          }
        );
      }
    

    AuthGuard

    @Injectable()
    export class AuthGuard implements  CanActivate {
    
      constructor(private authService: AuthService, private router: Router) {
      }
    
      canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
    
        if (this.authService.authInfo$.getValue().isLoggedIn()) {
          return true;
        }
    
        return this.authService.getAuthInfo()
          .map( (authInfo: AuthInfo) => authInfo.isLoggedIn())
          .do(allowed => {
            if (!allowed) {
              this.router.navigate(['/signin']);
              return false;
            } else {
              return true;
            }
          }).take(1);
      }
    }