Jakob Kreinecker
Jakob Kreinecker

Reputation: 111

Angular Async Router Guards

We have a Router Guard which checks if a user is logged in and admin It should also check if a request we send to a server is has the right result.

The problem is the canActivate function is finished before the server request is finished, so the Router Guard is always false.

I hope you have a solution for this problem

 canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    var user = JSON.parse(localStorage.getItem('currentUser'));
    if (user != null&&!user.isNotAdmin) {
      if(this.userService.isLoggedIn(user)){
          return true;
      }else{
          return false;
      }
    }else{

    // not logged in so redirect to login page with the return url
    this.router.navigate([''], { queryParams: { returnUrl: state.url } });
    return false;
    }

Upvotes: 8

Views: 7312

Answers (2)

Eyeslandic
Eyeslandic

Reputation: 14900

Later versions of Angular can return an Observable from the canActivate method, making this much easier. If the user is not found in localStorage you return immediately, if a user is found however you call your service method. The pipe and tap is used to intercept it so to speak, and if it returns false it also navigates to ''.

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<any> {
  const user = JSON.parse(localStorage.getItem('currentUser'));
  if (user === null || user.isNotAdmin) {
    return false;
  }

  return this.userService.isLoggedIn(user).pipe(
    tap((isLoggedIn) => {
      if (!isLoggedIn) {
        this.router.navigate([''], { queryParams: { returnUrl: state.url } });
      }
    })
  }
}

PS: Checking for admin status from localStorage is not very secure, but of course depends on your project whether it is acceptable risk.

Upvotes: 0

aryaag
aryaag

Reputation: 110

You have to use async/await to ensure canActivate waits till the resolution of your server request. Here's some sample code to help you:

/* UserService */
isLoggedIn(user): Observable<boolean> {
  // check if user is logged in
  return isUserLoggedIn;
}

/* Guard */
async canActivate(route: ActivatedRouteSnapshot, 
  state: RouterStateSnapshot): Promise<boolean> {
const user = JSON.parse(localStorage.getItem('currentUser'));
if (user !== null && !user.isNotAdmin) {

  const isUserLoggedIn: boolean = await this.userService.isLoggedIn(user).toPromise();
  // toPromise() converts Observable to Promise (async/await only works on Promises)
  // the code 'waits' at the above line till the server request is resolved

  return isUserLoggedIn;

} else {
  // not logged in so redirect to login page with the return url
  this.router.navigate([''], { queryParams: { returnUrl: state.url } });
    return false;
}

References if you want to read further:

Upvotes: 4

Related Questions