`Angular 6 and the expiration of Jwt tokens`

I am currently developing an angular application that utilizes jwt for authenticating database calls. However, I encountered a problem where, when the token expires on the server, the app starts displaying blank pages instead of the expected data. This happens because the expired token is still stored in the local storage. After some investigation, I came across the jwt2 library which can be used to track token expiry. Even after implementing this solution, I still have to manually refresh the page to redirect to the login page. While I am able to navigate between components, I would like the login page to automatically appear or the token to be refreshed as soon as it expires. If the user attempts to move between components with an expired token, they should be redirected to the login page or have the token refreshed. I'm unsure of what else needs to be done in this situation. Any assistance would be greatly appreciated. Thank you in advance.

Below is the code snippet for my authentication guard:

Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  constructor(private router: Router,private authService:AuthService ){ }

  canActivate(

    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    if (!(this.authService.isTokenExpired()) ){
      // logged in so return true
      console.log("Logged IN");
      return true;
    }

    // not logged in so redirect to login page with the return url
    this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
    return true;
  }
}

Here is the auth service implementation:


const helper = new JwtHelperService();

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  constructor(private http: HttpClient) { }

  public login<T>(username: string, password: string): Observable<HttpResponse<T>> {
    let headers = new HttpHeaders();
    const clientId = 'clientid';
    const secret = 'secret';
    headers = headers.append('Authorization', 'Basic ' + btoa(`${clientId}:${secret}`));
    headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
    const params = new HttpParams().set('username', username).set('password', password).set('grant_type', 'password').set('scope', 'read');
    return this.http.post<T>('/oauth/token', params.toString(), {
      headers,
      observe: 'response'
    });
  }

  public logout<T>() {
    this.http.post('/oauth/revoke_token', '', {}).subscribe();
  }

  getToken(): string {
    return localStorage.getItem(TOKEN_NAME);
  }

  isTokenExpired(token?: string): boolean {
    if(!token) token = this.getToken();
    if(!token) return true;

    const date = helper.getTokenExpirationDate(token);
    console.log(date);
    if(date === undefined) return false;
    return !(date.valueOf() > new Date().valueOf());
  }
}

Error interceptor implementation:

@Injectable()
export class H401Interceptor implements HttpInterceptor {

    constructor(private authService: AuthService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(catchError(err => {
            if (err.status === 401) {
                // auto logout if 401 response returned from api
                // this.authService.logout();
                // location.reload(true);
                localStorage.removeItem('currentUser');
            }

            const error = err.error.message || err.statusText;
            return throwError(error);
        }));
    }
}

Answer №1

If you encounter a '401 Unauthorized' response from the backend, consider using HttpInterceptor to handle it by deleting the token and redirecting to the sign-in page. Below is an example of how this can be implemented:

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    request = request.clone({
      setHeaders: {
        Authorization: `Bearer ${this.storageService.retrieve(tokenKey)}`,
        'Content-Type': 'application/json'
      }
    });
    return next.handle(request).pipe(
      catchError((err, caught) => {
          if (err.status === 401){
            this.handleAuthError();
            return of(err);
          }
          throw err;
       })
    );
}

private handleAuthError() {
    this.storageService.delete(tokenKey);
    this.router.navigateByUrl('signIn');
}

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

Tips for maintaining the order of an array in an observable, even when the elements are called randomly

I am facing a challenge with an array of objects where some require a service call and others do not. The objects that need a service call take longer to populate, resulting in an unordered array of objects. I have experimented with async/await, promises, ...

Changes in model not reflected in the view

In my Angular app (5.2.3), I have implemented a feature to display a login/logout button in the top right corner of the page. The functionality involves silently logging the user in using an external Open ID Connect provider and then displaying the user&ap ...

Enforcing alias types in TypeScript arguments is necessary for maintaining consistency and clarity

I'm currently facing a challenge with type unions and aliases. I have an alias for values that can possibly be null or undefined, along with a function that handles these values. Everything is running smoothly and safely. However, there are instances ...

In an array where the first 3 images have been filtered using an if statement, how can I show the image at the 3rd index (4th image) starting from the beginning?

In my personal web development project, I'm using AngularJS for the front-end and .NET Core for the back-end to create a website for a musical instrument retailer. The model I'm working with is called "Guitar" and all the guitar data is stored in ...

What is the best approach for managing optional object input parameters while also verifying the presence and accuracy of that specific property?

What is the best approach to handling a situation where a method has optional object member properties for the options object, but you still want to ensure the presence of that property with a default value in the resulting instance? Is creating a separate ...

Child component received an incorrect input value

Utilizing both the "YearComponent" and "StatPeriod" components has presented some challenges for me. Within my YearComponent, I invoke StatPeriod as follows: <app-save-stat-period [dateBegin]="dateBegin" [dateEnd]="dateEnd" byMonth bestNAverage></ ...

Using Angular 2 to assign unique ids to checkbox values

Is there a way to retrieve the value of a checkbox in Angular 2 without needing an additional Boolean variable? I am trying to toggle the enabled/disabled state of an input field based on the selection of a checkbox. While I know this can be accomplished ...

Setting input autofocus in a recursive Angular2 component

Currently, I am working on creating a tree view system similar to Workflowy for practice purposes. The simple version is operational; however, I am facing difficulty in setting the input focus when adding a new component. I have experimented with various ...

Utilizing TypeScript with Express for dynamic imports

import * as dotenv from 'dotenv'; dotenv.config(); const env: string = process.env.NODE_ENV || 'development'; export const dynamicConfig = async () => { const data = await import('./config.' + env); console.log('d ...

How do I go about updating my custom element after making a REST call using @angular/elements?

Looking for some assistance with my latest creation: import { Component, ViewEncapsulation, OnInit, } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject } from 'rxjs'; @C ...

The journey of an Angular and NGXS store application culminates in a seamless loop of store updates

Currently, I am utilizing NGXS as the store mechanism in my application. Within the store, there is a list of conditions that are displayed using the RuleEngineAddViewStepperConditionComponent component and the *ngFor directive on the UI. Each condition ha ...

Testing the unit with a customized header in the interceptor

I've been struggling to execute a unit test within an Angular 6 interceptor, but despite numerous attempts, I keep encountering the following error: Error: Expected one matching request for criteria "Match by function: ", found none. I'm rela ...

Issue with getStaticProps in Next.js component not functioning as expected

I have a component that I imported and used on a page, but I'm encountering the error - TypeError: Cannot read property 'labels' of undefined. The issue seems to be with how I pass the data and options to ChartCard because they are underline ...

HTML5 Drag and Drop: How to Stop Drag and Drop Actions from Occurring Between a Browser and Browser Windows

Is it possible to restrict HTML5 Drag & Drop functionality between different browsers or windows? I am currently working on an Angular2 app that utilizes native HTML5 Drag and Drop feature. Is there a way to prevent users from dragging items out of th ...

Managing relationships within TypeORM's single table inheritance using a base class for targeting relations

In my application, I aim to provide users with notifications in the form of news items of various types. The relationship between User and NewsItem needs to be one-to-many, with NewsItem serving as a base class for different types of news items. Below is ...

Axios JWT fails to transmit

I am currently working on a project that has two layers - the back-end is developed using Spring Boot with security measures in place such as Sprint Security and JWT, while the front-end is built using Vue.js with Axios library for inter-layer communicatio ...

Leverage the power of Filesaver.js in conjunction with Angular

I've searched through all the articles I could find about integrating Filesaver JS with Angular, but I'm still struggling to find a solution that works for me. In my system.config.js file, I included the following code in the map section: ' ...

Having Issues with CDK Virtual Scrolling in Angular Material Table

Dealing with an angular material table that contains millions of records can be quite challenging. I have implemented pagination with various options such as 10, 25, 50, 100, 500, and 1000 items per page. However, when selecting the option for 1000 or all ...

React Native is throwing a TypeError because it is encountering an undefined object

React Native is throwing an error claiming Undefined is not an object when it's clearly an object!! I'm confused about what's happening. Take a look at the code snippet below. Scroll down to the render() function. You'll see the follow ...

Limit the typescript generic type to only a singular string literal value, preventing the use of unions

Within my project, I have introduced a generic entity type that utilizes a generic to determine a field type based on a specific set of string literals: type EntityTypes = 'foo' | 'bar' | 'baz'; type EntityMappings = { foo: ...