I'm securing my Angular 6 app with JWT tokens. I have an auth service to secure UI elements:
export class AuthService {
constructor(
private http: HttpClient,
private router: Router,
public jwtHelper: JwtHelperService,
) {}
isAuthenticated(): boolean {
return !this.isTokenExpired;
}
which I can then call in my components:
<span class="page-nav" *ngIf="!authService.isAuthenticated()">
<a mat-button routerLink="/login">
Log In
</a>
</span>
I also have an auth guard for my routes:
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): boolean {
if (!this.authService.isAuthenticated()) {
this.router.navigate(['/login']);
return false;
}
return true;
}
}
This all works fine, except when the token becomes invalid, then the current page does not route to login
.
For instance, if I login, get a valid JWT token, I can navigate to a protected page, and protected UI elements appear (navigation etc.). If I then remove the JWT token from local storage via chrome dev tools, and click on the navigation, the protected UI elements disappear as expected, but the protected page stays, the route auth guard is not called, and only routes to login
when navigating to a different page.
I've tried routing in the isAuthenticated()
authService method, but this causes an infinite loop. How can I automatically route to login
outside of calling the AuthGuard?
EDIT
routing:
const routes: Routes = [
{ path: '', redirectTo: '/products', pathMatch: 'full' },
{
path: 'products',
component: ProductsComponent,
canActivate: [AuthGuard],
children: [
{ path: ':id', component: ProductComponent, canActivate: [AuthGuard] },
],
},
{
path: 'search',
component: SearchComponent,
canActivate: [AuthGuard],
},
{ path: 'login', component: LoginComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
EDIT: I realise that this is the expected behaviour, as @GangadharJannu mentions, the router is not aware that the UI elements have called the isAuthenticated()
method and are now hidden as it is returning false
, not until the AuthService refresh method is called and carries out the same check. I'm interested to know the best way to let the router know to navigate to login
when the UI elements call the isAuthenticated()
method?
EDIT: My working solution:
import { map } from 'rxjs/operators';
import { interval } from 'rxjs';
import { JwtHelperService } from '@auth0/angular-jwt';
...
public validateToken(): void {
interval(1000).pipe(
map(() => {
const isExpired: boolean = this.jwtHelper.isTokenExpired(
localStorage.getItem('jwt_token'),
);
if (isExpired) {
this.logout();
}
}),
);
}