1

I want to do some validation on each refresh request for some routes. So I'm using Angular AuthGuard. The problem is in canActivate method I want to perform validation with online API.

API is /token/verify which simply gets token variable (jwt) and verify if it's true or false. Then if verify is not complete will route to /login page else do the rest.

Here is the code:

canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
let token = this.auth.getToken();
let url = '/token/verify/';
if (!token) {
  this.router.navigate(['/login']);
  return false;
}
this.auth.verifyToken().then(res => {
  return true;
}).catch(err => {
  this.router.navigate(['/login']);
  return false;
})

and the verifyToken method is like this:

verifyToken(): Promise<boolean> {
    let token = this.getToken();
    return !token ?
        new Promise<boolean>(resolve => {
            resolve(false);
            return false;
        }) :
        this.http.post(this.url + '/token/verify/', { 'token': token })
            .toPromise()
            .then((res) => {
                localStorage.data = JSON.stringify(res);//(res.json());
                return true;
            }
            ).catch((error) => {
                return false
            });
}

Problem is that the promise call doesn't work and will be passed. I mean the first part that does check it from localstorage works fine. But the next part that checks it with online API will not work. and authGuard doesn't wait for its response to do the routing.

I guess it should be in some async/await manner. But I did some tests and none of them worked. Will be glad if anyone could help me.

Reza Torkaman Ahmadi
  • 2,958
  • 2
  • 20
  • 43

1 Answers1

3

I think you'll be able to simplify the whole implementation if you use Observables instead.

If you do consider that proposal, then your verifyToken method could be refactored to this:

import { of, Observable } from 'rxjs';

verifyToken(): Observable<boolean> {
  const token = this.getToken();
  return token ? this.http
    .post(this.url + '/token/verify/', {
      'token': token
    })
    .pipe(
      tap(res => localStorage.data = JSON.stringify(res)),
      map(
        res => true,
        error => false
      )
    ) : of(false)
}

And then in your Guard, you'd simply do this:

canActivate(
  next: ActivatedRouteSnapshot,
  state: RouterStateSnapshot
): Observable < boolean > | Promise < boolean > | boolean {
  const token = this.auth.getToken();
  const url = "/token/verify/";

  if (!token) {
    this.router.navigate(['/login']);
    return false;
  }

  return this.auth.verifyToken().pipe(
    catchError(err => {
      console.log('Handling error locally and rethrowing it...', err);
      this.router.navigate(['/login']);
      return of(false);
    })
  );

}

Here's a Working Demo for your ref.

SiddAjmera
  • 38,129
  • 5
  • 72
  • 110
  • appreciate for spending time to write a detailed response. But can you check the canActivate last `return` statement. It seem some change needed for it. It gives this error `Argument type MonoTypeOperatorFunction is not assignable...` Can u check it again plz. – Reza Torkaman Ahmadi Dec 22 '19 at 06:48
  • @RezaTorkamanAhmadi, please try the updated answer. I've placed the `if` condition in `{}` – SiddAjmera Dec 22 '19 at 06:58
  • No, still the same error. Shouldn't i use `pipe` and `catch` methods instead? – Reza Torkaman Ahmadi Dec 22 '19 at 07:06
  • @RezaTorkamanAhmadi, sorry about that. I've updated my answer with the correct code and a Sample Working Demo for your ref. Please check if that helps. – SiddAjmera Dec 22 '19 at 07:24
  • 1
    I was able to make it work with `catchError` Also. Please verify my changes too. really appreciate it. with your help i was able to solve it. :) – Reza Torkaman Ahmadi Dec 22 '19 at 07:28