Search code examples
angularangular-routing

Angular 2 Router - CanActivate Guard


I'm implementing a CanActivate guard to redirect the user to the login page if their session is invalid.

The check as to whether the session is valid or not is done through a service and so from the guard I'm subscribing to the service call to get the session validity state.

I've debugged the code and everything seems to be working as it should, in fact when the session is invalid the app does redirect back to the login page. However when I return true from the guard, the page is not loaded.

My code is similar to what is listed on the Angular documentation (Code on Plunker).

I'm not sure whether I've implemented something wrong in the guard itself. The difference that I can see is that the guard is accessing a property in the service - but I don't think that should matter much. From what I can understand returning true from the guard would simply continue the normal routing operation.

Note: I'm using Angular RC3

Code:

Guard

export class MyGuard implements CanActivate {
    constructor(private checkSessionService: CheckSessionService, private router: Router) {}

    canActivate(
    next:  ActivatedRouteSnapshot,
    state: RouterStateSnapshot
    ) {
        this.checkSessionService.checkUserSession(localStorage.getItem('userToken'))
        .subscribe(validSessionReturn => {

            if (validSessionReturn) {
                return true;
            } else {
                // redirect to login
                this.router.navigateByUrl('/login');
                return false;
            }
        },
        error => console.log(error));
    }
}

Edit

I've investigated further and it looks like the issue I'm having is related to the fact that I'm trying to return the boolean from the subscribe method itself. As a test I've tried the following and everything worked as expected:

export class MyGuard implements CanActivate {
    constructor(private checkSessionService: CheckSessionService, private router: Router) {}

    canActivate(
    next:  ActivatedRouteSnapshot,
    state: RouterStateSnapshot
    ) {
        if(localStorage.getItem('userToken') !== null) {
            return true; // or return Observable.of(true);
        } else {
            return false; // or return Observable.of(false);
        }
    }
}

You can notice from the above code that one can return either a boolean or an Observable<boolean>. The CheckSessionService I've implemented does return an Observable<boolean>. In fact when I tried the code below, once again everything worked as expected, meaning that if the session is valid the page is loaded successfully, otherwise routing is stopped:

export class MyGuard implements CanActivate {
    constructor(private checkSessionService: CheckSessionService, private router: Router) {}

    canActivate(
    next:  ActivatedRouteSnapshot,
    state: RouterStateSnapshot
    ) {
        return this.checkSessionService.checkUserSession(localStorage.getItem('userToken'));
    }
}

However with the above example I can't find a way to redirect to a specific page based on the value retrieved by the service. My question now is whether there's a way to check the result of this.checkSessionService.checkUserSession(localStorage.getItem('userToken')) without a subscribe method, or there's a different way how to return from the subscribe method. I did try:

if(this.checkSessionService.checkUserSession(localStorage.getItem('userToken')) === Observable.of(false))

but as expected it did not work.


Solution

  • Replace

    this.checkSessionService...
    

    by

    return this.checkSessionService...
    

    Instead of subscribing to the observable and return the subscrption, you must also return the observable itself, but plug something in case it's false:

    return this.checkSessionService.checkUserSession(localStorage.getItem('userToken'))
        .do(‌​function(authenticated) { 
            if (!authenticated) ... 
        });