angulartypescriptroutesguardmsal

Angular load user roles from core before use guard


please, I actually have an app.module that loads the core module via lazy loading. And I load the roles in the core component to the store. Then I have a role guard that goes into the store to look at those roles. When I load some /home page and then go to the page where I have the role guard applied, everything is ok because the roles are already loaded. However, when I type the url into the browser directly to the one that the role guard is applied to, it fuckups. It loads the app.module, but then it doesn't load the core.module anymore, it loads the guard. Where aren't the roles, and even when I tried it through a skipWhile like that, it still didn't work. Any idea what to do about it? Thanks a lot

AppRoutingModule

  {
    path: '',
    children: [{
      path: '', loadChildren: () => import('./core/core.module').then(m => m.CoreModule)
    }]
  },

CoreRoutingModule

{
    path: '',
    component: CoreComponent,
    children: [
      {
        path: "test,
        loadChildren: () => import('@modules/test/test.module').then(m => m.TestModule),
        canActivate: [MyGuard]
    ......

CoreComponent

  ngOnInit(): void {
    this.authFacade.initAll();
    this.authFacade.loginCheck();
  }  

AuthGuard - only for test

canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> {
    return this.authFacade.user$.pipe(
      skipWhile(user => user === undefined),
      take(1),
      map(user => {
        console.log(user);
        if (!user)
          return false;

        return true;
      })
    )
  }

Solution

  • This may be because the roles are being loaded asynchronously in your CoreComponent and the guard is being evaluated before the roles are fetched and stored.

    You can try this:

    //CoreComponnet
    ngOnInit(): void {
      this.authFacade.initAll();
      this.authFacade.loginCheck();
    
      this.authFacade.rolesLoaded$.pipe(
    // wait until roles are loaded
        filter(loaded => loaded), 
    // only happens once
    take(1) 
      ).subscribe(() => {
        // roles loaded
      });
    }
    
    

    and

    //AuthGuard
    canActivate(
      route: ActivatedRouteSnapshot,
      state: RouterStateSnapshot
    ): Observable<boolean> {
      return combineLatest([
        this.authFacade.user$,
     // something to track roles loading
        this.authFacade.rolesLoaded$
      ]).pipe(
        skipWhile(([user, rolesLoaded]) => user === undefined || !rolesLoaded),
        take(1),
        map(([user, rolesLoaded]) => {
          if (!user) {
            return false;
          }
    
          // check user's roles against necesary roles for the route
          // add logic here to see if the user has the required roles
    
          return true;
        })
      );
    }