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,