1

I have an angular 4 app, and have implemented a guard to handle authentication. We have different modules which, use a factory to create what we call a ThemeService. Each module is self contained application with their own styles, and security. Each module tells the ThemeService what role the user must have to access and the guard works well.

Here's the guard:

 export class AuthenticatedGuard implements CanActivate
    {
        constructor (private memSrv:MembershipService,
private themeSrv:ThemeService,private router:Router )
        {
        }
        canActivate(
            route: ActivatedRouteSnapshot,
            state: RouterStateSnapshot
        ): Observable<boolean> | Promise<boolean> | boolean {

            return this.memSrv.GetCurrentUser()
                .map(u => {
                    if (!u) {
                        this.router.navigate([this.themeSrv.LoginUrl]);
                        return false;
                    }
                    var isAuth = u.Capabilities.some(s => s === this.themeSrv.AuthCapability);
                    if (isAuth) {
                        return true;
                    }
                    else {
                        //TODO: This is an unauthorized but authenticated user it should go to an unauthorized page
                        this.router.navigate(["/Unauthorized", { url: state.url }]);
                        return false;
                    }
                }
                );


        }
    }

I now have a module which has some routes which need to be secured using a different role. My question is how I can pass to the AuthenticationGuard the role that must be checked for each route. I'd hate to have to create a different guard for each role we want to check.

Bellow is a snippet of my route configuration

   {
        path: "",
        component: Component.MainComponent, 
        children:
        [
            {
                path: 'attachmentsTest',
                component: Component.AttachmentsTestComponent,
            },
            {
                path:"home",
                component: Component.HomeComponent,
                canActivate: [AuthenticatedGuard],
                resolve: {
                    user: CurrentUserResolver
                }
            }, 
JoshBerke
  • 66,142
  • 25
  • 126
  • 164
  • Could you set it in the route's data property? – DeborahK Jan 22 '18 at 23:55
  • You can put the role in localStorage when changing route and retrieve it in guard? – Powkachu Jan 23 '18 at 15:35
  • @DeborahK not sure can you put values in the route configuration to pull out latter if so that would work – JoshBerke Jan 23 '18 at 18:56
  • You can read the current route and decide which role has to be checked. But I don't think that would be the best practice. In my opinion, doing Guard per Role would be the Best Practice. and if there is a similar code you don't want to copy, you can put them either in a general Guard that would be called in the parent route or in an external service. – Samy Sammour Jan 23 '18 at 19:42
  • @SamySammour yes but currently we have around 30 different roles to secure against and the list is growing all the time. If we only had a few that would be one thing... – JoshBerke Jan 23 '18 at 20:00
  • Well, this is a very interesting question with lots of scenarios. I will think again tomorrow and will research a bit and I will write to you if there is new comes in mind – Samy Sammour Jan 23 '18 at 22:46
  • I have just an idea, I am not sure if it will solve the problem but it could lead to something. I am thinking of creating one Guard and inject it into the module with multiple values, "ex. 30 providers" and each one has a unique name and a unique role. But they all call the same guard with the different information. So One-Guard / Multi-Providers I was doing the same trick in .NET Dependency Injection. Theoretical it could work! – Samy Sammour Jan 25 '18 at 11:43

1 Answers1

3

Yes, you can put values into the route configuration to pull out later using the data property. Here is an example:

@NgModule({
  imports: [
    RouterModule.forChild([
      { 
        path: 'products',
        component: ProductListComponent,
        data: { pageTitle: 'Product List' }
      },
      { path: 'products/:id', component: ProductDetailComponent },
      { path: 'products/:id/edit', component: ProductEditComponent }
    ])
  ],  // ...

Notice the data property here.

You can then read that property using syntax like this:

this.pageTitle = this.route.snapshot.data['pageTitle'];
DeborahK
  • 57,520
  • 12
  • 104
  • 129
  • Would you consider it the best practice in that case? Well, in my opinion, making multi guards for each section would be better. would it be safe to use the route snapshot to store a role? – Samy Sammour Jan 23 '18 at 19:44
  • That question really depends. I don't know enough about this specific scenario to determine whether it is the best approach. But any time you need to duplicate very similar code, to me that is not best practice because maintenance becomes difficult. There there is nothing wrong with defining a bit of data with the data property. – DeborahK Jan 23 '18 at 20:00
  • and since I'm securing the route, putting that data in the route makes sense to me – JoshBerke Jan 24 '18 at 15:56