Routes Guards causing wrong Route Navigation behavior

1.1k views Asked by At

I'm having an issue trying to navigate through different routes.

I have two different route modules.

app.routes.ts:

Containing only LoginPage:

export const routes: Routes = [
  {
    path: 'login',
    component: LoginPageComponent,
    canActivate: [PreventLoggedInAccess]
  },
  {
    path: '',
    redirectTo: 'login',
    pathMatch: 'full'
  },
  {
    path: '**',
    redirectTo: 'login'
  }
];

export const Routing: ModuleWithProviders = 
    RouterModule.forRoot(routes, { useHash : true });

With PreventLoggedInAccess.canActivate, that, if the user is already logged in redirects him to the logged in section with /app prefix and child route home. It's defined as:

canActivate(): boolean {
  if (!this._authService.isAuthenticated()) {
      return true;
  }
  this._router.navigate(['/app/home']);
  return false;
}

pages.routes.ts:

Containing all /app subroutes that are accessible only if the user is logged in. This is achieved using AuthGuardService.canActivateChild:

export const pageRoutes: Routes = [
  {
    path: 'app',
    component: PagesComponent,
    canActivateChild: [AuthGuardService],
    children: [
      { path: '', redirectTo: 'home', pathMatch: 'full' },
      { path: 'home', component: HomePageComponent },
      { path: 'contents', component: ContentsComponent },
    ]
  }
];

export const Routing: ModuleWithProviders = RouterModule.forChild(pageRoutes);

With the latter that redirects to /login if the user isn't logged in. It's defined as:

canActivateChild(): boolean {
  if (this._authService.isAuthenticated()) {
      return true;
  }
  this._router.navigate(['login']);
  return false;
}

When I navigate from app/home to app/contents it only goes to ContentsComponent after navigating two times. So, if I do two times this._router.navigate(['app/components']); it works, if I do it only one time, the route changes from app/home to app/route for 1ms and it returns to app/home, while if I do it a second time it changes route. While, if I'm in app/contents and try to navigate to app/home it changes route just fine.

isAuthenticated works fine. Both authguards work just fine, so, if I try to access any app child route when I'm not logged In I get redirected to login and If I try to access login when I'm logged In I get redirected to app/home.

I managed to debug a bit and I noticed the following flow:

  • First attempt - app/home -> app/contents:
    • navigate(['app/contents']) is called
    • PreventLoggedInAccess.canActivate is called
    • AuthGuardService.canActivateChild is called
  • Second attempt - app/home -> app/contents:
    • navigate(['app/contents']) is called
    • AuthGuardService.canActivateChild is called

Of course the expected behavior is the second one.

EDIT

Removing this._router.navigate([/app/home]); from PreventLoggedInAccess.canActivate solves the issue

canActivate(): boolean {
  if (!this._authService.isAuthenticated()) {
      return true;
  }
  return false;
}

But still, I do not understand why PreventLoggedInAccess.canActivate is called when navigating to an app child even though AuthGuardService.canActivateChild is attached to it? Why is it called only on the first attempt?

1

There are 1 answers

0
Rajat Gupta On BEST ANSWER

You have mentioned pageRoute can have all subchild of app so its routing file should contain that module.

So that concept of lazy loading and guards can be properly used. I have answered this by assuming that your pageRoute module is child of App.

I suggest to use the AuthGuard only one time. AuthGaurd should be used on module containing other modules or components and not be on login component itself.

Here page module is lazy loaded and can be activated only if authguard returns true In case of false user will navigated to login.

app.route.ts

const routes: Routes = [
  {
    path: '',
    loadChildren: './pages/pages.module#PagesModule',
    canActivate: [AuthGuardService]
  },
  { path: 'login', loadChildren: './login/login.module#LoginModule' },

];

AuthGuard

canActivateChild(): boolean {
  if (this._authService.isAuthenticated()) {
      return true;
  }
  this._router.navigate(['/login']);
  return false;
}

Page.route.ts

const routes: Routes = [
  {
    path: '', component: PageComponent,
     children: [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', component: HomePageComponent },
  { path: 'contents', component: ContentsComponent },


     ]
  }
];

In case you want to debug in current routing condition you can use Augury from the Chrome web store.