0
votes

I'm trying to verify a token in the back end before accessing a route

this is my authentication service:

export class AuthService {

  private registerUrl = 'http://127.0.0.1:3000/register';
  private loginUrl = 'http://127.0.0.1:3000/signin';
  private verifyTokenUrl = 'http://127.0.0.1:3000/v';

  constructor(private http: HttpClient) { }


  isLoggedIn() {
    if (!localStorage.getItem('token')) {
      console.log('false in isloggedin method');
      return false;
    }
    console.log('logged in, not verified yet');
    return true;
  }

  isTokenValid(token): Observable<boolean> {
    console.log('verifing');
    return this.http.post<any>(this.verifyTokenUrl, {token});
  }

  getToken() {
    return localStorage.getItem('token');
  }
}

guard:

export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService,
              private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    if (this.authService.isLoggedIn()) {
      const token = localStorage.getItem('token');
      this.authService.isTokenValid(token)
      .pipe(
        map(e => {
          if (e) {
            return true;
          } else {
            this.router.navigate(['/login']);
            return false;
          }
        }),
        catchError((err) => {
          this.router.navigate(['/login']);
          return of(false);
        })
      );
    } else {
      this.router.navigate(['/login']);
      return of(false);
    }
  }
}

it seems that the observable does not activate "like when you use the subscribe method" and the code in the API didn't even run. The canActivate() suppose to accept an Observable as returned value. The guard will wait for the Observable to resolve and look at the value. If 'true' it will pass the check, else ( any other data or thrown error ) will reject the route. referring to this answer

in my case, the app stays at the current page and doesn't route to the target, and doesn't post to the server as well. there is also a similar issue here and they solved it by adding .take(1) but in my case it gives an error Property 'take' does not exist on type 'OperatorFunction<{}, boolean>'

here is my verify API just in case

//.........token verify
app.post('/v', (req, res) => {
    const {token} = req.body
    const payload = jwt.verify(token, 'secretKey' )
    console.log(payload);
    if (!payload) {
        return res.status(401).send(false)
    }
    res.status(200).send(true)
})
2

2 Answers

2
votes

You're not returning your observable:

this.authService.isTokenValid(token)
  //...

—>

return this.authService.isTokenValid(token)
  //... 
2
votes

Try implementing your route guard returning an observable:

export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService,
    private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    return Observable.create(observer => {
      if (this.authService.isLoggedIn()) {
        const token = localStorage.getItem('token');
        this.authService.isTokenValid(token)
          .pipe(
            map(e => {
              if (e) {
                observer.next(true);
              } else {
                this.router.navigate(['/login']);
                observer.next(false);
              }
            }),
            catchError((err) => {
              this.router.navigate(['/login']);
              observer.next(false);
            })
          );
      } else {
        this.router.navigate(['/login']);
        observer.next(false);
      }
    });
  }
}

Additionally, don't forget to implement your interceptor as a provider in your app.module:

// your.module.ts
// ...
providers: [
  ...,
  AuthGuard
];

And on the route you want to guard:

// some route to guard
{ path: 'guarded-route', component: GuardedRouteComponent, canActivate: [AuthGuard] }