3

I am using NgRx store for authentication, my problem is with routes and their children. the guard that I am using is for role authentication and at the same time I check if the user is still authenticated on the server by dispatching IsAuthenticated action. the problem starts when I access a child route, the guard fires the action twice, one for the parent and one for the child!. How can I avoid this duplicate action dispatch? I tried .take(1) but it allows the action to get fired only once and totally get ignored subsequently.

this is a snapshot of my guard service code,

@Injectable()
export class RoleGuardService implements CanActivate {
  constructor(private store: Store<fromApp.AppState>, private router: Router) {
  }
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>|Promise<boolean>|boolean {
    this.store.dispatch(new authActions.IsAuthenticated(fromAuth.authId));
    let isInRole = false;
    let isAuth: boolean;
    let userDashboardUrl: string;
    let userRoles: string[];
    this.store.select(c => c.auth.entities[fromAuth.authId]).subscribe(
      (data: Auth) => {
        isAuth = data.authenticated;
        userRoles = data.userData.roles;
        userDashboardUrl = data.userData.dashboardUrl;
      });
    const expectedRole: Array<string> = (route.data.expectedRole['roles']).map(
      (roles) => {
        return roles.toUpperCase();
      }
    );
    if (userRoles) {
      userRoles = (userRoles).map(
        (data) => {
          return data.toUpperCase().substring(0, data.length - 1);
        }
      );
      isInRole = expectedRole.some(r => userRoles.indexOf(r) !== -1);
    } else {
      isInRole = false;
    };

     if (! isAuth) {
       this.store.dispatch(new authActions.Logout(fromAuth.authId, fromAuth.invalidAuth, state.url));
    }
     if ( isAuth && ! isInRole ) {
      this.router.navigate([userDashboardUrl]); // redirect to user dashboard
    }
     return isInRole;
  }
}

my parent route,

const dbRoutes: Routes = [

    {
      path: '', component: DbHomeComponent, data: { preload: true }, children: [
        {
          path: 'registry',
          canActivate: [RoleGuardService],
          data: { expectedRole: { roles: ['reg'] } },
          loadChildren: 'app/core/database/database-cores/registry-core/modules/registry.module#RegistryModule',
      }, .....

and then the child route,

const routes: Routes = [

    { path: '', component: RegistryOutletComponent, children: [
        {path: '', component: DashboardComponent},
        {
            path: 'patientregistration',
            component: PatientRegistrationFormComponent,
            canActivate: [RoleGuardService],
            data: { expectedRole: { roles: ['reg5','reg4'] } },
        },
    ]},
];

and my side effect,

 @Effect()
  onIsAuthenticatedUser = this.actions$
    .ofType(AuthActions.ISAUTHENTICATED)
    .map((action: AuthActions.IsAuthenticated) => { return action })
    .switchMap((action: AuthActions.IsAuthenticated) => {
      const actionPayload = action;
      return this.httpService.getRequest('Account/IsAuthenticated')
        .catch(() => { return Observable.empty() })
        .switchMap((isAuth: HttpResponse<boolean>) => {
          if (!isAuth.body) {
            return Observable.of(new AuthActions.Logout(fromAuth.authId, fromAuth.invalidAuth, actionPayload.returnUrl));
          } else {
            return Observable.concat(
              Observable.of(new AuthActions.Authenticated(fromAuth.authId, this.fetchUserData()),
            ),
             Observable.empty());
          }
        });
    });

here where I noticed the problem,

Duplicate action

JSON
  • 1,583
  • 4
  • 31
  • 63

0 Answers0