Angular Route Guards: Or vs. And

2.4k views Asked by At

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?

1

There are 1 answers

2
Kirk Larkin On

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.