I have a bit of a pickle.
I am using Route guard (implementing CanActivate
interface) to check if user is granted access to particular route:
const routes: Routes = [
{
path: '',
component: DashboardViewComponent
},
{
path: 'login',
component: LoginViewComponent
},
{
path: 'protected/foo',
component: FooViewComponent,
data: {allowAccessTo: ['Administrator']},
canActivate: [RouteGuard]
},
{
path: '**',
component: ErrorNotFoundViewComponent
}
];
Now it works great in protecting the '/protected/foo' route from activating, but I would like to tell the user that route he is trying to access is forbidden (similar to 403 Forbidden you may get from server).
The problem:
How do I show the user this special error view without redirecting him to error route which seams to be the preferred option by so many sources I have found?
And how do I still use my RouteGuard
without actually loading the forbidden route, because if I check access inside my FooViewComponent
and display different view it kind of defeats point of having RouteGuard
in the first place.
Ideally I would like to have my RouteGuard
not only returning false in canActivate()
method, but also replace component completely with say ErrorForbiddenViewComponent
. But I have no idea how to do it, or is it event possible. Any alternatives?
This is how my route guard looks now:
import {Injectable} from '@angular/core';
import {Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
import {AuthService} from '../services/auth.service';
@Injectable()
export class RouteGuard implements CanActivate {
constructor(
private router: Router,
private auth: AuthService
) {}
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const { auth, router } = this;
const { allowAccessTo } = next.data;
const identity = auth.getIdentity();
if (
identity &&
allowAccessTo.indexOf(identity.role)
) {
// all good, proceed with activating route
return true;
}
if (identity) {
// TODO show ErrorForbiddenViewComponent instead of redirecting
console.log('403 Forbidden >>', next);
}
else {
// not logged in: redirect to login page with the return url
const [returnUrl, returnQueryParams] = state.url.split('?');
console.log('401 Unauthorised >>', returnUrl, returnQueryParams, next);
router.navigate(['/login'], {queryParams: {returnUrl, returnQueryParams}});
}
return false;
}
}
So I am just preventing route from loading, but I am not redirecting. I only redirect non logged visitors to login route.
Reasoning:
Does anyone have some kind of solution for this? I also wonder how come that after Angular 2+ being around for so long nobody had this kind of situation before? Everybody is just ok with redirecting?
Also keep in mind that although I am currently using the FooViewComponent
synchronously, that may change in future!
I had once worked on the similar problem.
Sharing my stackblitz poc where I have created -
/auth
route is provided with PermissionGuardService
guard)The guard is evaluating the user type and handling the redirection / error accordingly.
The use cases are -
shows a toast with log in message
)shows a toast with unauthorised message
)show a toast with success messaage
)I have stored the user in local storage.
Let me know if you need a special handling in it and I will update the code base.
Cheers!