Angular router: navigating to different routes according to the user's role

2.1k views Asked by At

I'm writing a mock e-commerce app which has the following structure:

  • app
    • auth
      • sign-in-page
      • sign-up-page
      • auth-routing.module.ts
        const routes: Routes = [
          {
            path: 'signup',
            component: SignUpPage,
          },
          {
            path: 'signin',
            component: SignInPage,
          },
        ];
        
      • auth.module.ts
    • admin
      • root-page
      • admin-routing.module.ts
        const routes: Routes = [
          {
            path: 'admin',
            component: RootPage,
            children: [
              // ...                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
            ],
          },
        ];
        
      • admin.modules.ts
    • customer
      • root-page
      • catalog-page
      • check-out-page
      • thank-you-page
      • customer-routing.module.ts
        const routes: Routes = [
          {
            path: 'customer',
            component: RootPage,
            children: [
              { path: 'catalog/:categoryId', component: CatalogPage },
              { path: 'checkout', component: CheckOutPage },
              { path: 'thankyou', component: ThankYouPage },
            ],
          },
        ];
        
      • customer.module.ts
    • page-not-found
    • app-routing.module.ts
      const routes: Routes = [
        { path: '**', component: NotFoundPage },
      ];
      
    • app.module.ts
    • app.component.html
    • app.component.css

The basic workflow is supposed to be as follows:

  1. The user navigates to the root path /.
  2. The application detects that they are not logged in, so it redirects them to /signin.
  3. They enter their credentials and press Sign In.
  4. If the authentication is successful,
    1. If the user is an admin, they get redirected to /admin.
      1. admin-router.module.ts redirects them to some sub-path of /admin.
    2. If the user is a customer, they get redirected to /customer.
      1. customer-router.module.ts redirects them to /customer/catalog/<default category ID>.
      2. They put some products in the shopping cart and proceed to /customer/checkout.
      3. They place the order and get redirected to /customer/thankyou.

What I'm not sure about is how to accomplish the redirection following a successful log-in. Obviously it has to be done in two stages: first to some common path such as / and then either to /customer or to /admin depending on the user's role. The second stage probably needs to be handled by app-routing.module.ts, perhaps using a guard, but I'm not sure exactly how to do it.

EDIT (2021-04-20)

The problem can be summarized as follows:

What I need is a way (preferably declarative) to redirect the application from / to one of the following paths depending on its state:

State Path
Logged out /auth
Logged in as a customer /customer
Logged in as an admin /admin
2

There are 2 answers

4
Ron Inbar On BEST ANSWER

What I ended up doing is this:

// app-routing.module.ts

const routes: Routes = [
  {
    path: '',
    canActivate: [AuthorizationGuard],
    children: [],
  },
  { path: '**', component: NotFoundPage },
];
// authorization.guard.ts

@Injectable({
  providedIn: 'root',
})
export class AuthorizationGuard implements CanActivate {

  constructor(private router: Router, private authService: AuthService) {}

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): UrlTree {
    const user = this.authService.getLoggedInUser();
    return (
      (user?.role === 'admin' && this.router.parseUrl('/admin')) ||
      (user?.role === 'customer' && this.router.parseUrl('/customer')) ||
      this.router.parseUrl('/auth')
    );
  }

}
1
Robin Dijkhof On

You could create some dummy component and protect it with a guard that handles the correct navigation. Something like this:

[
  {
    path: 'loggedin',
    component: DummyComponent,
    canActivate: [HandleLoginGuard]
  },
  {
    path: 'admin',
    component: AdminComponent,
  },
  {
    path: 'customer',
    component: CustomerComponent,
  }
]


@Injectable()
class HandleLoginGuard implements CanActivate {
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ) {
    if(isAdmin()){
        route.navigate(/admin);
    }else{
        route.navigate(/customer);
    }
    return false;
  }
}

This way you can put the logic in a guard instead of the login component.