Search code examples
angularangular-routerangular-router-guards

Using "redirectTo" and "canMatch" in routing config to redirect users based on permissions


I'm trying to configure a set of routes where the top level URL in a module should redirect users to an alternate path in the same module based on their current permissions. I think I should be able to accomplish what I want by using the redirectTo parameter and a canMatch guard.

This is an example of the functionality I want. In this scenario I have a menu that directs users to a "members" list module. Based on their permissions, I want the user to see either all app members (they are an admin), or just their own team (they own teams).

What I expect to happen is that if they are not an admin, the first path will fail to match, and so the redirect will be ignored. The second empty pattern will be tried, it will pass the "canMatch" guard, and then the redirect will be processed. The user will then be redirected to "./teams".

Instead, what I'm seeing is the first redirectTo being processed no matter what the result of the guard is.

const routes: Routes = [
  {
    path: '',
    redirectTo: 'all',
    pathMatch: 'full',
    canMatch: [AdminGuard],
  },
  {
    path: '',
    redirectTo: 'teams',
    pathMatch: 'full',
    canMatch: [TeamOwnerGuard],
  },
  {
    path: 'all',
    pathMatch: 'full',
    component: AllMembersListComponent,
  },
  {
    path: 'teams',
    pathMatch: 'full',
    component: TeamMembersListComponent,
  },
  {
    path: '**',
    component: NotFoundComponent,
  },
];

@NgModule({
  imports: [
    RouterModule.forChild(routes),
  ],
})
export class TeamMembersModule {}

Documentation

redirectTo: "A URL to redirect to when the path matches."

canMatch: "An array of CanMatchFn or DI tokens used to look up CanMatch() handlers, in order to determine if the current user is allowed to match the Route. "

Update

Update for anyone else that ends up here. As of Angular 15, redirectTo is evaluated before any route guards, and so what I was attempting in the original question will not work.

Solution

The solution that worked for me was to build a canActivate guard that returns a UrlTree on the base route. The UrlTree redirects to an alternate route based on permissions.


Solution

  • The solution is moving the canMatch routes to the final routes.

    For the tests I have run:

    1. When you have a empty path route, angular flats and combine the route with his redirectTo path as his children.
    2. Run all canMatch guard in the redirectTo child. If any of the canMatch guars returns false and jumps to the next empty path route and repeats the process.

    Here is my example app: Stackblitz