Philipp
Philipp

Reputation: 13

Current State of JWT in Angular

im using Angular for a Project/Task management application with a user authentification. For this I set a JWT.

My problem, if the jwt is not valid any more or i destroy it in the local storage, im still able to navigate to the guardes routes and i the the for example user profil with empty fields. I recieve 500 or 401 from the backend, because the user is missing.

How can I check the current state of the jwt while navigating in the application?

app.component.ts:

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']);
  }



}

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);
  }

}

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('eingeloggt');
      }else {
       return false;
       console.log('ausgeloggt');
    }
  }
  canActivateChild(
    childRoute: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return true;
  }

}

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');
  }

}

Upvotes: 1

Views: 356

Answers (2)

Philipp
Philipp

Reputation: 13

I updated the "validateToken" and "isSignedin". They didnt work. It seems to work now

 public isAuthenticated(): boolean {
    const token = localStorage.getItem('auth_token');

    if (token) {
      if(this.jwtHelper.isTokenExpired(token)){
        return false;
      }
      return true;
    }else{
     
      return false;
    }

  }

Upvotes: 0

Mustapha Afkir
Mustapha Afkir

Reputation: 64

2 things: First: Have you set de guard in the routes canActivate property? For example:

{ path: 'tasks', component: TaskListComponent, canActivate: [LoginGuard] },

Second: don't you need to redirect the user to /login if isloggedIn is false?

For example:

if(this.isLoggedin==true){
        return true;
        console.log('eingeloggt'); // code after return will not run!!
      }else {
       this.router.navigate('/login'); // you need to have this route in your routes
       return false;
       console.log('ausgeloggt'); // code after return will not run!!

Upvotes: 1

Related Questions