The Evolution of JSON Web Tokens in Angular

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

}

Answer №1

Two important points to consider:

First: Have you implemented the guard in the routes' canActivate property correctly?

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

Secondly, should you not redirect the user to /login if they are not logged in?

For instance:

if(this.isLoggedin==true){
        return true;
        console.log('Logged in'); // code after return will not execute!!
      }else {
       this.router.navigate('/login'); // make sure you have a route set up for this
       return false;
       console.log('Logged out'); // code after return will not execute!!

Answer №2

I made some changes to the methods "validateToken" and "isSignedin", as they were not functioning properly. It appears that everything is now working correctly.

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

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

  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What are the steps for setting up Angular IDE on Ubuntu?

I am new to angular2 and I'm having trouble installing the angular IDE on my system. When I try to install it using npm, I encounter errors. Command : - npm install -g angular-ide Error Message:- npm WARN deprecated <a href="/cdn-cgi/l/email-p ...

Angular Material table connected to Firebase displaying data, pagination and sorting functionality appear but are not functional

I am facing an issue with my Angular Material table that is populated with data from a Firebase database. The paginator and sorting functionalities are both visible, but they don't work when I try to use them (sort or change items viewed). How can I ...

Thumbnail Alert: Currently experiencing technical difficulties. Services temporarily unavailable

Greetings to the Stack Overflow Community! I am currently encountering a challenge and would greatly appreciate any guidance. In my situation, I have an image stored on OneDrive and I am using the following link to fetch a specific thumbnail of the image ...

Unlocking the potential of the ‘Rx Observable’ in Angular 2 to effectively tackle double click issues

let button = document.querySelector('.mbtn'); let lab = document.querySelector('.mlab'); let clickStream = Observable.fromEvent(button,'click'); let doubleClickStream = clickStream .buffer(()=> clickStream.thrott ...

What could be causing my code to not run after subscribing to the observables?

In my code, I have two methods that return two lists: one for accepted users and the other for favorite users. The first part of my code works well and returns both lists, but in the second part, I need to filter out the accepted users who are also on the ...

The rendering of code is often disrupted when utilizing the keyword const

I've been working my way through the Angular2 tutorial called Tour of Heroes and everything has been going smoothly up until this point. At the link above, I've encountered a problem. The code on the left is what the tutorial recommends, but fo ...

Error: Unable to assign value to property 12 because the object does not support extensibility

I'm facing an issue with my client application as I cannot figure out the error I am encountering. Despite successfully subscribing to a GraphQL subscription and receiving updates, I am struggling to update the TypeScript array named "models:ModelClas ...

Is it possible to have OpenLayers with multiple colored shapes on a single layer?

After successfully implementing the code to add shapes to the map, I noticed an issue with changing the style of the vectorLayer. Here's a snippet of the draw function: addInteraction() { this.coreMap.map.addInteraction(this.modifyLayer); t ...

Properly typed tuple parameter for a function's return value

Is there a way to modify a function to return its value based on the type of the argument as a tuple? For instance, I need to adjust the getAll function so that it returns values based on an argument provided in a tuple. type PathImpl<T, Key extends key ...

Creating a URL using Angular 2

I'm interested in creating a dynamic URL in my component using variables. I want the URL to be generated based on these variables. For instance: "https://www.youtube.com/results?search_query=skijumper.name" So if my variable skijumper.name = "Ahonen ...

Is it possible to register a component for destruction through code?

When Angular destroys a component, it automatically calls the ngOnDestroy method if it is implemented. Is there a way to register a component for destruction with Angular without explicitly implementing the OnDestroy method? I am interested in customizin ...

The Yup Resolver suddenly threw an error indicating that the type was not able to be assigned

I have encountered an error while using @hookform/resolvers version 3.9.0. Here is the code snippet that caused the issue: const form = useForm<FormType>({ defaultValues: { ...defaultFormFieldValues }, resolver: yupResolver(schema), mode: ...

Unpacking objects in Typescript

I am facing an issue with the following code. I'm not sure what is causing the error or how to fix it. The specific error message is: Type 'CookieSessionObject | null | undefined' is not assignable to type '{ token: string; refreshToken ...

Tips on saving a cookie using universal-cookie

I followed a solution on Stack Overflow to set a cookie in my React application. However, the cookie expires with the session. Is there a way I can make this cookie persist beyond the session so it remains even when the browser is closed and reopened? ex ...

Using a class as an interface in TypeScript is a common practice when a constructor is

Consider a scenario where I have a class structured like this: class MyClass { a: string } Now, let's say I create a variable with the following definition: let obj: MyClass = { a: 2 } An error will be triggered in Typescript because 2 is not ...

Can you explain the concepts of observables, observers, and subscriptions in Angular?

As I dive into Angular, I find myself tangled in the concepts of observables, observers, and subscriptions. Could you shed some light on these for me? ...

The most effective method for monitoring updates to an array of objects in React

Imagine a scenario where an array of objects is stored in state like the example below: interface CheckItem { label: string; checked: boolean; disabled: boolean; } const [checkboxes, setCheckboxes] = useState<CheckItem[] | undefined>(undefined ...

What is the best way to inform the user of their login status in Angular?

As a newcomer to angularfire2, I am currently working on implementing authentication and providing the user with feedback on their login status. I am using version angularfire2": "^5.0.0-rc.4. I came across an example on this site, but I am unsure of how i ...

An effective way to define the type of a string property in a React component using Typescript

One of the challenges I'm facing is related to a React component that acts as an abstraction for text fields. <TextField label="Enter your user name" dataSource={vm} propertyName="username" disabled={vm.isSaving} /> In this set ...

Angular and D3.js: Enhancing StackedBar Chart ToolTips with Added Functionality

I have modified the code from this example to be compatible with the latest versions of Angular and D3. Although it works well after a small tweak, I am unable to see the tooltips when hovering over the chart... https://i.sstatic.net/Rpebe.png <h3> ...