Using Angular 14, I have routes setup as like this:
{ path: 'users', component: UsersComponent, canActivate: [AuthGuard], data: { role: 'user.read' } },
Then in the AuthGuard I return false if the authenticated jwt doesn't contain a 'user.read' scope. This works and if the user doesn't have the scope, they are redirected to '/'.
Now I want to remove the 'users' page button from the UI; I have the router-link users
, how can I access the route data to check if the page will work; and hide the button if not.
I think essentially I need the ActivatedRouteSnapshot (as passed to CanActivate) for the given url - all the examples I see require a Router subscription; but that only gives me the current url; not the one I might visit.
All other answers on SO seem to be basically suggesting I add a check for 'users.read' on the button; but I really don't want to add this again - it's already defined in the route.
Seems like a simple problem, but I'm struggling for an answer - thanks!
CodePudding user response:
You could move the logic that checks for the user.read
scope into a service, and then inject the service into both the AuthGuard
and your component. Then you just call the function inside the guard's canActivate
function, and use it to drive an *ngIf
for the button in your component.
CodePudding user response:
I've written this function to solve the problem; this isn't the full answer, but with this function I can get the route data for a given url and compare the scopes easily.
Seems mad I have to write this - this code is obviously somewhere in the Router object code, but I can't find it! Still hoping for a better answer; but this will get me by, and may help someone else!
This function handles routes with custom matchers and simple parameterised routes, but I'm sure there are many other route options not covered
It gets the list of routes from router.config
; declared in the class constructor as private router: Router
matchRouteToUrl(url: string): Route | null {
if (url[0] == '/') { url = url.slice(1); }
let urlRoute = this.router.config.find(route => {
if (route.path == url) {
return true;
}
const urlSegmentGroup = this.router.parseUrl(url).root.children[PRIMARY_OUTLET];
if (urlSegmentGroup) {
const urlSegments = urlSegmentGroup.segments;
if (route.matcher) {
return route.matcher(urlSegments, urlSegmentGroup, route);
}
if (urlSegments.length > 1 && route.path?.includes('\:')) {
const pathSegmentGroup = this.router.parseUrl(route.path).root.children[PRIMARY_OUTLET];
if (pathSegmentGroup) {
const pathSegments = pathSegmentGroup.segments;
if (urlSegments.length == pathSegments.length) {
let allSegmentsMatch = true;
for (let i = 0; i < urlSegments.length; i ) {
if (pathSegments[i].path[0] != ':' && urlSegments[i].path != pathSegments[i].path) {
allSegmentsMatch = false;
}
}
return allSegmentsMatch;
}
}
}
}
return false;
});
return urlRoute || null;
}