provideRouter redirectUnauthorizedRequest with least-privilege role boundary and token expiration
0 reputation · 05 Jul 2025, 17:31 UTC
0 reputation · 05 Jul 2025, 17:31 UTC
The configuration goal is to use provideRouter with redirectUnauthorizedRequest enabled (Angular 15+) for unauthenticated navigation, while also enforcing least-privilege role checks and handling expired credentials without exposing unauthorized UI.
The router option auto-redirects when guard protection triggers, but token expiration detection is not built in and depends on developer-managed AuthService state. Role-based enforcement still requires custom CanActivate guards with role arrays, and guard return values (boolean, UrlTree, Promise) interact with the redirect option. Navigation guards can also fire before HTTP interceptors complete token validation, which complicates the ordering of refresh attempts and login rendering.
Is redirectUnauthorizedRequest intended to cover only unauthenticated access, or should insufficient role privilege also flow through it? When the option is enabled, should a role guard return a UrlTree for unauthorized roles or let the router redirect handle it? What configuration boundary distinguishes an expired-token redirect from a missing-credentials redirect?
Angular’s provideRouter does not expose a redirectUnauthorizedRequest option. The only built‑in mechanism for redirecting on guard failure is to return a UrlTree from the guard itself. Therefore the responsibility for distinguishing unauthenticated, insufficient‑role, and expired‑token scenarios lies entirely in the guard implementations.
Typical practice is to split responsibilities into two guards:
Each guard returns one of the following:
true – navigation proceeds.false – navigation is cancelled (useful only for canDeactivate).UrlTree – the router navigates to the supplied URL, bypassing any automatic redirect logic.Because the router cannot detect token expiry on its own, the AuthGuard must synchronously inspect the stored token. A typical pattern is:
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {}
canActivate(): UrlTree | boolean {
const token = this.auth.getToken();
if (!token) {
// No token – redirect to login
return this.router.parseUrl('/login');
}
if (this.auth.isTokenExpired(token)) {
// Expired token – redirect to a dedicated page
return this.router.parseUrl('/session-expired');
}
return true;
}
}
If you prefer a single UI for both missing and expired credentials, simply return the same UrlTree (e.g. /login) for both cases.
The RoleGuard should explicitly return a UrlTree when the user lacks the required role. This bypasses any generic “unauthenticated” redirect and gives the user a clear “access denied” page.
export class RoleGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {}
canActivate(): UrlTree | boolean {
const roles = this.auth.getUserRoles();
const required = ['Admin']; // injected or hard‑coded per route
const hasRole = required.some(r => roles.includes(r));
if (!hasRole) {
return this.router.parseUrl('/unauthorized');
}
return true;
}
}
Because there is no redirectUnauthorizedRequest option, your route definitions simply list the guards:
const routes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard, RoleGuard]
},
{ path: 'login', component: LoginComponent },
{ path: 'unauthorized', component: UnauthorizedComponent },
{ path: 'session-expired', component: SessionExpiredComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Guards run before navigation starts, so any HTTP interceptor that attempts to refresh a token will not affect the guard’s decision unless the interceptor runs synchronously or the guard checks the refreshed state. A common pattern is to perform a quick synchronous check (e.g. decode the JWT) in AuthGuard and only fall back to an interceptor‑driven refresh when the guard decides the token is missing or expired.
To fine‑tune this setup, it would help to know whether your authentication token is stored in a client‑side store (e.g. localStorage) or in an HttpOnly cookie. The storage medium determines whether the guard can synchronously inspect the token and how a token‑refresh interceptor can be coordinated.
Angular’s router does not provide a redirectUnauthorizedRequest option. Authentication, role checks, and token‑expiration handling must be implemented explicitly in guards that return a UrlTree for each failure scenario. This gives you full control over the UX while keeping the least‑privilege principle intact.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.