angularangular-route-guards

How to wait for result from fetch (promise) in CanActivateFn guard to get user data from API in Angular project?


I need to implement a CanActivateFn guard to block non authenticated users in an Angular project.

I tried different approaches and shaped this guard so far.

import { inject } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  CanActivateFn,
  Router,
  RouterStateSnapshot,
  UrlTree,
} from '@angular/router';
import { IUser } from '../model/user.model';

export const authGuard: CanActivateFn = (
  route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot
): Promise<boolean | UrlTree> | boolean | UrlTree => {
  let isAuthenticated = false;

  let baseUrl: string = 'https://localhost:44411/';

  let currentUser = <IUser>{};

  // I need to get user info syncronusly
  fetch(baseUrl + 'Account/GetUser')
    .then((response) => response.json())
    .then((json) => {
      currentUser = jsonas IUser;
    })
    .catch((error) => {
      console.error('There was an error to get user information.');
    });

  isAuthenticated = currentUser.isAuthenticated;

  // 👇 Redirects to login route
  const isAnonymous = !isAuthenticated;

  if (isAnonymous) {
    return inject(Router).createUrlTree(['/', 'login']);
  }

  return isAuthenticated;
};

The issue is that I need to find a way to get user data synchronously to be able to do validation based on a user data.

Please suggest how to achieve the requirement or suggest a different approach. Note: I need to use fetch to get user data.


Solution

  • Convert the promise to an observable, then validate the logic on a map if error catch using catchError present then navigate.

    import { inject } from '@angular/core';
    import {
      ActivatedRouteSnapshot,
      CanActivateFn,
      Router,
      RouterStateSnapshot,
      UrlTree,
    } from '@angular/router';
    import { IUser } from '../model/user.model';
    import { lastValueFrom } from 'rxjs';
    import { map, catchError } from 'rxjs/operators';
    import { HttpClient } from '@angular/common/http';
    
    export const authGuard: CanActivateFn = (
      route: ActivatedRouteSnapshot,
      state: RouterStateSnapshot
    ): Promise<boolean | UrlTree> | boolean | UrlTree => {
      let baseUrl: string = 'https://localhost:44411/';
      // I need to get user info syncronusly
      return lastValueFrom(
        inject(HttpClient).get(baseUrl + 'Account/GetUser')
      ).pipe(
        map((currentUser: any) => {
          return !currentUser.isAuthenticated
            ? inject(Router).navigate(['login'])
            : false;
        }),
        catchError(() => inject(Router).navigate(['login']))
      );
    };
    

    using fetch

    import { inject } from '@angular/core';
    import {
      ActivatedRouteSnapshot,
      CanActivateFn,
      Router,
      RouterStateSnapshot,
      UrlTree,
    } from '@angular/router';
    import { IUser } from '../model/user.model';
    import { lastValueFrom } from 'rxjs';
    import { map, catchError } from 'rxjs/operators';
    import { HttpClient } from '@angular/common/http';
    
    export const authGuard: CanActivateFn = (
      route: ActivatedRouteSnapshot,
      state: RouterStateSnapshot
    ): Promise<boolean | UrlTree> | boolean | UrlTree => {
      let baseUrl: string = 'https://localhost:44411/';
      // I need to get user info syncronusly
      return lastValueFrom(fetch(baseUrl + 'Account/GetUser')
    .then((response) => response.json())).pipe(
        map((currentUser: any) => {
          return !currentUser.isAuthenticated
            ? inject(Router).navigate(['login'])
            : false;
        }),
        catchError(() => inject(Router).navigate(['login']))
      );
    };