Search code examples
angularangular-guards

Angular AuthGuard does not return UrlTree from inside subscription


I am trying to implement UrlTree to redirect user if guard fails. this.authService.isAuthenticated() returns observable.

The following does not work but it does console false.

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree {
  this.authService.isAuthenticated().subscribe(isAuthenticated => {
    if (isAuthenticated) {
      return true;
    } else {
      console.log(isAuthenticated);
      return this.router.createUrlTree(['auth/sign-in']);
    }
  });
  return false;
}

But the following works if I remove subscription:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree {
  return this.router.createUrlTree(['auth/sign-in']);
}

Solution

  • You cannot return from inside subscribe (in any case). Route guard can return a boolean or an observable of a boolean. So in your case you need to return an observable of boolean. Worth mentioning also, in your current code, since this is async, what you are currently returning is always false no matter the logged in status, why? It takes x amount of time to get the authenticated status, so false is returned every time, angular does not wait for the response, just moves on to the next code available, which is return false; in your current code.

    So, as said, return an observable, i.e use map instead of subscribe:

    import { map, take } from 'rxjs/operators';
    
    // ....
    
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
      return this.authService.isAuthenticated()
        .pipe(
          take(1),
          map(isAuthenticated => {
            if (isAuthenticated) {
              return true;
            }
            // no need for else, if above is truthy, further code is not executed
            this.router.createUrlTree(['auth/sign-in']);
            return false;
          })
        );
    }