Currently, I am developing a Project/Task management application using Angular that requires user authentication. To achieve this, I have implemented a JWT (JSON Web Token).
One issue I encountered is that even if the JWT becomes invalid or is removed from local storage, users can still access protected routes and view profile pages with empty fields, resulting in backend errors like 500 or 401 due to missing user information.
Is there a way to verify the validity of the JWT while navigating within the application?
In my app.component.ts file:
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { TokenAuthService } from './shared/token-auth.service';
import { AuthenticationStateService } from './shared/authentication-state.service';
import { JwtService } from './shared/jwt.service';
import { User } from './user';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
isLoggedin: boolean | undefined;
user!: User;
constructor(
public router: Router,
private tokenAuthService: TokenAuthService,
public authenticationStateService: AuthenticationStateService,
public jwtService: JwtService
) {
if (this.isLoggedin){
this.jwtService.profile().subscribe((res:any) => {
this.user = res;
})
}
}
ngOnInit() {
this.authenticationStateService.userAuthState.subscribe(res => {
this.isLoggedin = res;
});
}
logOut() {
this.authenticationStateService.setAuthState(false);
this.tokenAuthService.destroyToken();
this.router.navigate(['signin']);
}
}
In authentication-state.service.ts:
import { Injectable } from '@angular/core';
import { TokenAuthService } from '../shared/token-auth.service';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
@Injectable({
providedIn: 'root'
})
export class AuthenticationStateService {
private userCurrentState = new BehaviorSubject<boolean | undefined>(this.tokenAuthService.isSignedin());
userAuthState = this.userCurrentState.asObservable();
constructor(
public tokenAuthService: TokenAuthService
) { }
setAuthState(value: boolean) {
this.userCurrentState.next(value);
}
}
In login.guard.ts:
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { User } from '../user';
import { AuthenticationStateService } from './authentication-state.service';
import { JwtService } from './jwt.service';
@Injectable({
providedIn: 'root'
})
export class LoginGuard implements CanActivate, CanActivateChild {
isLoggedin: boolean | undefined;
user!: User;
constructor(
public authenticationStateService: AuthenticationStateService,
public jwtService: JwtService
) {
this.jwtService.profile().subscribe((res:any) => {
this.user = res;
})
this.authenticationStateService.userAuthState.subscribe(res => {
this.isLoggedin = res;
});
}
ngOnInit() {
}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
if(this.isLoggedin==true){
return true;
console.log('logged in');
}else {
return false;
console.log('logged out');
}
}
canActivateChild(
childRoute: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return true;
}
}
In token-auth.service.ts:
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { environment } from 'src/environments/environment';
@Injectable({
providedIn: 'root'
})
export class TokenAuthService {
private tokenIssuer = {
login: environment.apiUrl + '/api/auth/signin',
register: environment.apiUrl + '/api/auth/signup'
}
constructor(
public router: Router,
) { }
setTokenStorage(token: string){
localStorage.setItem('auth_token', token);
}
getJwtToken(){
return localStorage.getItem('auth_token');
}
// Validate token
validateToken(){
const token = this.getJwtToken();
if(token){
const payload = this.payload(token);
if(payload){
return Object.values(this.tokenIssuer).indexOf(payload.iss) > -1 ? true : false;
}else {
return false;
}
} else {
return false;
}
}
payload(token: string) {
const jwtPayload = token.split('.')[1];
return JSON.parse(atob(jwtPayload));
}
// User state
isSignedin() {
return this.validateToken();
}
// Destroy token
destroyToken(){
localStorage.removeItem('auth_token');
}
}