Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 CanActivate guard not working

I'm using the Observable<boolean> return for canActivate(). The following function was set up for testing, and it resolves correctly, the component displays.

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
  return Observable.from([{ admin: true }]).map(x =>
  {
    if (x.admin) return true;
    return false;
  });
}

However, the behavior of the actual code is that I stay on the login component despite the console output indicating the route should activate. The only real difference to the test above is I'm calling a service this.auth.isAdmin() instead of using Observable.from. The result of this.auth.isAdmin() is an Observable<boolean> with a value of true.

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
  const isAdmin$ = this.auth.isAdmin();
  return isAdmin$.map(x =>
  {
    console.log('isAdmin returned ' + x);
    if (!x) {
      console.log('redirectToLogin');
      this.auth.redirectToLogin(state.url);
      return false;
    } else {
      console.log('canActivate = true');
      return true;
    }
  });  
}

Here is the routing:

{
  path: 'admin',
  canActivate: [AdminGuard],
  children: [
    ...adminRoutes
  ]
},

Here is my console output:

isAdmin returned false
admin-guard.service.ts:27redirectToLogin
auth.service.ts:36 navigating to stored path "/admin"
auth.service.ts:21 Object {isAdmin: true, isPaid: false, $key: "xYFs8kMDpKdYKxDw4AL21FtnSWn1"}
admin-guard.service.ts:25 isAdmin returned true
admin-guard.service.ts:31 canActivate = true

And here is the isAdmin() function in case that is of interest:

isAdmin(): Observable<boolean> {
    if (!this.auth) return Observable.from([false]);
    const uid = this.auth.uid;
    return this.af.database.object(`user/${uid}`).do(x => console.log(x)).map(x => x.isAdmin);
}
like image 632
Ryan Langton Avatar asked Mar 12 '23 03:03

Ryan Langton


1 Answers

The observable returned by your isAdmin function does not complete. AngularFire2 FirebaseObjectObservable instances do not complete; they emit objects as the underlying data changes.

Observables returned by guards have to complete. You can ensure this by using first (or take(1)) to complete the observable when the first value is emitted:

canActivate(
  route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot
): Observable<boolean> {
  const isAdmin$ = this.auth.isAdmin();
  return isAdmin$.first().map(x =>
  {
    console.log('isAdmin returned ' + x);
    if (!x) {
      console.log('redirectToLogin');
      this.auth.redirectToLogin(state.url);
      return false;
    } else {
      console.log('canActivate = true');
      return true;
    }
  });  
}

At the time of writing, it was necessary for the returned observable to complete. However, Angular now calls first on the returned observable, so there is no longer a requirement for the observable to complete.

like image 156
cartant Avatar answered Mar 20 '23 15:03

cartant