Navigating in Angular when encountering an HTTP 401 Error

Here is the code snippet I've been working on:

AuthenticationService

import { Injectable } from '@angular/core';

import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';

@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {
  public currentUser: string

  constructor(
    private http: HttpClient, 
    private route: ActivatedRoute,
    private router: Router,
    ) { }

  login(username:string, password:string){
    return this.http.post<any>('http://localhost:8000/api/token/', {username: username, password: password})
      .pipe(
        map(data => {
          localStorage.setItem('access_token', data.access)
          localStorage.setItem('refresh_token', data.refresh)
        })
      )
  }

  logout(){
    localStorage.removeItem('access_token')
    localStorage.removeItem('refresh_token')
  }

  getJWToken(){
    return localStorage.getItem('access_token')
  }

  getRefreshToken(){
    return localStorage.getItem('refresh_token')
  }

  refreshToken(){
    let refreshToken : string = localStorage.getItem('refresh_token'); 

    return this.http.post<any>('http://localhost:8000/api/token/refresh/', {"refresh": refreshToken}).pipe(
      map(data => {
        localStorage.setItem('access_token', data.access)
      },
        error => {
          this.router.navigate(['/login']);
        }
      )
    )
  }
}

TokenInterceptor

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, first, switchMap } from 'rxjs/operators';
import {AuthenticationService} from '../services/authentication.service'

@Injectable()
export class TokenInterceptor implements HttpInterceptor {

  constructor(public authService : AuthenticationService ) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {    
      return next.handle(this.addToken(request, localStorage.getItem('access_token')))
      .pipe(catchError(error => {
        if (error instanceof HttpErrorResponse && error.status === 401) {
          return this.refreshToken()
          .pipe(switchMap(
            data => next.handle(this.addToken(request, localStorage.getItem('access_token')))
          ))
        } else {
          return throwError(error);
        }
      }));
  }

  private addToken(request: HttpRequest<any>, token: string) {
    return request.clone({
      setHeaders: {
        'Authorization': `Bearer ${token}`
      }
    });
  } 

  private refreshToken(){
    return this.authService.refreshToken()
  }
}

I'm facing a challenge with error handling in my AuthenticationService's refreshToken method. How can I properly catch errors, particularly when a 401 error occurs, and redirect to the login page? Any suggestions on how to solve this issue would be greatly appreciated.

Answer №1

Have you ever wondered if this code gets executed?

        this.refreshToken()
          .pipe(switchMap(
            data => next.handle(this.addToken(request, 
                  localStorage.getItem('access_token')))
        ))

It seems like something is missing after the pipe:

   .subscribe( o => {});

An observable needs to be subscribed to in order to be triggered.

The catchError function in the pipe is used to handle errors and change the object when an error occurs. To catch exceptions, set an exception handler as the second argument of subscribe():

    this.refreshToken()
      .pipe(switchMap(
        data => next.handle(this.addToken(request, localStorage.getItem('access_token')))
      ))
      .subscribe( 
          o => { console.info(o); },
          err => { console.err(err); }
      );

A TokenInterceptor is a suitable place to add a token to all outgoing requests.

For additional checks and possible redirection, consider using a CanActivate guard:

Sample pseudo code:

@Injectable()
export class AuthGuardCanActivate implements CanActivate, CanActivateChild {

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

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return this.canActivateChild(route, state);
    }

    canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return this.checkLogin(state.url);
    }

    checkLogin(url: string): boolean {
        if (this.authService.isLoggedIn()) return true;
        this.router.navigate(['/login']);
        return false;
    }
}

Check out: https://angular.io/api/router/CanActivate

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

Angular2: The '@Component' decorator does not contain a 'directives' property in its configuration object

After creating a directive to auto-expand a textbox, I encountered an error when implementing it into the component. myAppComps.ts https://i.sstatic.net/rZHQc.png NPM RUN BUILD https://i.sstatic.net/DDY4k.png auto-grow.directives.ts https://i.sstat ...

Is it possible to determine the status of several angular components at once?

After following a tutorial to create a Tic-Tac-Toe app, I am now attempting to enhance its functionality independently. The draw condition is the current obstacle that I am facing. Each square in the tic-tac-toe grid is represented by its own Angular Comp ...

Steps for generating a signal that postpones the primary signal and promptly resets

I am looking to create a signal that will automatically switch to an underlying signal or memo after a specific delay, and reset immediately if the primary signal is cleared. The code snippet below illustrates my desired functionality. import { render } fr ...

Injecting a useFactory provider in Angular is a common practice

I manage a factory provider service that selects a service based on a flag. Everything works fine when I need a debug students service, but when I set the flag to false, the application throws an ERROR TypeError: serverService.fetchData is not a function. ...

ReactJS: error occurs when trying to fetch data and encountering issues with reading properties

I am currently attempting to initiate an API call (a GET request) in order to download a document. However, I am encountering an error when making the API call: TypeError: Cannot read properties of undefined (reading 'payload') const printPin ...

Angular 16 brings a revolution in routerLink behavior

Previously, when I was using angular < 16, my routes configuration looked like this: { path: Section.Security, canActivate: [AuthGuard, AccessGuard, AdminGuard], children: [ { path: '', pathMatch: 'full', ...

Version 1.9.3 of Redux Toolkit is encountering an error stating that the 'push' property is not found on the type 'WritableDraft<CartState>'

Currently delving into Redux Toolkit using TypeScript and facing a roadblock that seems deceptively simple. Unfortunately, my current knowledge isn't enough to unravel this puzzle and I'm in need of some guidance. The issue arises with an error ...

React Native is throwing an error message saying that the Component cannot be used as a JSX component. It mentions that the Type '{}' is not assignable to type 'ReactNode'

Recently, I initiated a new project and encountered errors while working with various packages such as React Native Reanimated and React Navigation Stack. Below is my package.json configuration: { "name": "foodmatch", "version ...

Creating a personalized state object containing unresolved promises in React Native utilizing axios inside a custom React Hook

I'm currently in the process of creating a custom state within a custom Hook for React Native (non-Expo environment). The state I am working on looks like this: interface ResponseState { api1: { error: boolean; errorMsg?: string; ...

tips for accessing the value outside of the subscription in angular2

Created a function within the component.ts file inside the constructor: constructor(private _visitService: VisitService,) { this._visitService.getchartData().subscribe(data => { this.fetchedData = data console.log("INSIDE SUBS ...

Changing elements transferred from component to service

Currently, I am in the process of refactoring my code by consolidating common component methods into a shared service. However, I have encountered an issue where I am unable to overwrite a component's public object property that is passed to the servi ...

Error message: "The function platform_browser_dynamic_1.bootstrap does not exist in Angular 2."

I had everything set up and running smoothly until suddenly I started receiving this error out of nowhere: TypeError: platform_browser_dynamic_1.bootstrap is not a function Here's the component I've been working on: import { Component, Input, ...

Encountering issue where the Angular application within the UWP project fails to load the second

I am facing a challenge with integrating my Angular project into a Universal Windows Platform (UWP) application. The Angular code functions flawlessly in the browser, but once incorporated into the UWP bundle, I encounter navigation issues. Technical Stac ...

Guide on Retrieving an Array from an Observable

Hey there! I've come across a function that is supposed to return an Array. In the function below, this.cordovaFile.readAsArrayBuffer(this.cordovaFile.dataDirectory, storageId) actually returns a Promise Array. I'm converting it into an Observabl ...

Angular 5 is throwing an invalid response during preflight

I am attempting to make an HTTP request to a server using the HTTP library in Angular 5 like so: return this.http.post('http://localhost/api-panel/index.php/admin-api-user/check-session', []); This is how I am subscribing to the HTTP observable ...

Viewability of external values in angular designs

Currently, I am facing an issue where multiple modules have duplicated options within the class field: ... options = ['opt1','opt1'] ... To solve this problem, I want to move the duplicated options to a constants module and then im ...

Browser inspect tool is failing to hit breakpoints when using USB-connected device

While troubleshooting my Ionic Capacitor application on a USB connected device, I noticed that my browser dev-tools (Chrome, Edge, Firefox) are not hitting my breakpoints in the source code. Interestingly, when I run the application on ionic serve, everyt ...

Troubleshooting issue: Unable to Subscribe to Subject in Angular 5+ with RxJS

My service has an observable that is subscribed to, and the payload is passed to a subject in the service. However, when I subscribe to the subject in my component, nothing happens. I expect to see the output from console.log in the .subscribe method withi ...

Tips for synchronizing response JSON with TypeScript interface in Angular 6

I am retrieving a list of files that have been uploaded from a backend endpoint, and it comes back in this format: [ { "filename": "setup.cfg", "id": 1, "path": C:\\back-end\\uploads\\setup.cfg", ...

Adjust the tooltip position on the Mat paginator

I'm attempting to adjust the positioning of the tooltip for mat-paginator so that it is closer to the pagination buttons. Currently, the tooltip is positioned too far away, as shown below: https://i.sstatic.net/XYD1j.jpg I've made attempts to m ...