4

I'm working on securing the front-end of my Angular application using route guards. In working with them in the past and researching online, adding multiple guards to a route requires all of them to return true to allow access.

But what if I want just one to return true to allow access? (like || instead of &&)

For example, my route guards look for certain roles in the user's token:

@Injectable()
export class ActiveRoleGuard implements CanActivate {
    constructor(private sessionService: SessionService, private router: Router) { }


    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        let token = this.sessionService.getToken();

        if (!token) {
             return false;
        }

        if (token.role.toLowerCase().indexOf("active") < 0) {
            this.router.navigate(["/issue"]);
            return false;
        }

        return true;
    }
}

And

@Injectable()
export class AdminRoleGuard implements CanActivate {
    constructor(private sessionService: SessionService, private router: Router) { }


    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        let token = this.appSessionService.getToken();

        if (!token) {
            return false;
        }

        if (token.role.toLowerCase().indexOf("admin") < 0) {
            this.router.navigate(["/issue"]);
            return false;
        }

        return true;
    }
}

If I was to combine them normally in a router module it would be something like...

{path: "someroute", component: SomeComponent, canActivate: [ActiveRouteGuard, AdminRouteGuard]}

... but that would require a user to be both Active and an Admin. But what if I wanted to enforce being Active and either an Admin or a Manager?

It's simple to enforce on the API like so:

[Authorize(Roles = "Active")]
[Authorize(Roles = "Admin, Manager")]
public class SomeController : ApiController

but how do I do this in Angular?

Kirk Larkin
  • 84,915
  • 16
  • 214
  • 203
Nathan Foss
  • 595
  • 1
  • 9
  • 34
  • You could do a single guard that would manage both cases, even though it's not that clean, I don't see any other solution yet... That said, it seems pretty weird for an admin not to have some token check as well. You could fix that by adding an `admin` role to your tokens – Alex Beugnet Sep 05 '17 at 20:41
  • I'd rather not combine because there's currently 6 roles with overlapping permissions. But if this isn't supported in Angular yet then that's my best option – Nathan Foss Sep 05 '17 at 20:44

1 Answers1

9

Rather than having two separate CanActivate implementations, you could just use a single version that can be "configured". To do this, you can take advantage of the data property of Route. For example, in your routes, you could do something like:

{
    path: "someroute",
    component: SomeComponent,
    canActivate: [RoleRouteGuard],
    data: {
        requiredRoles: ["Role1", "Role2", "Role3"]
    }
}

Using this, you could take one of your existing CanActivate implementations, and make it more generic. You can access the requiredRoles property from data via the ActivatedRouteSnapshot. e.g:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    const requiredRoles = route.data.requiredRoles;
    ....

Once you've got this, you can check if the token's role is in the array, etc, etc.

If you have an always-required Active role and then a one-of Admin or Manager role, you could also extend this to have multiple data properties. For example, you could have requiredRoles and atLeastOneOfRoles and update your logic to process that accordingly... There are many options at this point but I don't think you will need help with that.

Kirk Larkin
  • 84,915
  • 16
  • 214
  • 203