Troubleshooting Issue: Angular 6 - Authentication token interceptor not including headers

After experimenting with various approaches using Angular 6 for the past couple of days, I recently tried implementing a solution mentioned in this post: . Despite my efforts, the header in the requests still remains unset.

import {Inject, Injectable} from '@angular/core';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest,
  HttpErrorResponse,
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class AuthTokenInterceptor implements HttpInterceptor {
  constructor() {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).do((event: HttpEvent<any>) => {
      if (localStorage.getItem('id_token') != null) {
        // Cloning the request to add a new header.
        const request = req.clone({
          setHeaders: {
            'Content-Type' : 'application/json; charset=utf-8',
            'Accept'       : 'application/json',
            'Authorization': `Bearer ${localStorage.getItem('id_token')}`
          }
        });
        return next.handle(request);
      }
    }, (err: any) => {
      if (err instanceof HttpErrorResponse) {
        if (err.status === 401) {
          console.log('redirect auth interceptor')
          // Perform a redirect
        }
      }
    });
  }
}

Although logging out request reveals that the request.headers.lazyUpdate array has been updated with 3 items, the intercepted request does not seem to include the Authorization header.

request.headers.lazyUpdate:

{name: "Content-Type", value: "application/json; charset=utf-8", op: "s"}
{name: "Accept", value: "application/json", op: "s"}
{name: "Authorization", value: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2Mzh9.tLTmPK46NhXSuqoCfZKgZcrQWzlNqLMI71-G0iy3bi8", op: "s"}

(request.headers.headers appears to be empty---could this be the issue?)

app.module.ts:

providers: [
    {provide: HTTP_INTERCEPTORS, useClass: AuthTokenInterceptor, multi: true},
  ],

The reason I suspect an issue with the interceptor is that manually adding headers to the request eliminates the 401 error and yields the appropriate data response with a status of 200:

return this.http.get(environment.API_URL + 'list/supervise/' + encodeURIComponent(id),
      {headers: new HttpHeaders().set('Authorization', `Bearer ${localStorage.getItem('id_token')}`)}).pipe(
        map((res: any) => res.data)
    );

Could there be something crucial that I am missing here? Your input would be greatly appreciated.

EDIT:

In response to a comment below, I realized that I was invoking next.handle twice. Here is the revised solution I have implemented:

import {Injectable} from '@angular/core';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest
} from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthTokenInterceptor implements HttpInterceptor {
  constructor() {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('id_token');

    req = req.clone({
      setHeaders: {
        'Authorization': `Bearer ${token}`
      },
    });

    return next.handle(req);
  }
}

Answer №1

Why not simplify the code? Try a more basic version, similar to your reference link.

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const jwt = localStorage.getItem('id_token');
    if (!!jwt) {
     req = req.clone({
       setHeaders: {
         Authorization: `Bearer ${jwt}`
       }
     });
   }
   return next.handle(req);
 }

You don't need to handle errors here because the purpose of an interceptor in this context is to clone requests before sending them off with any modifications. You can add additional headers and data before the request is sent out and wait for it to return from the API without worrying about error handling. Leave the error handling to the service that calls the httpRequest (e.g. then, catch, pipe).

If you declare this in app.module.ts, all requests to the API in your app will be intercepted. What if you want to handle a specific request with the error message "Nothing here"? Complicated logic could affect all requests.

I haven't tried your code above, but nesting like that might cause issues. Consider putting breakpoints and debugging to see what's going wrong.

Answer №2

Here's the full solution I utilized:

import {Injectable} from '@angular/core';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest
} from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthTokenInterceptor implements HttpInterceptor {
  constructor() {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('id_token');

    req = req.clone({
      setHeaders: {
        'Authorization': `Bearer ${token}`
      },
    });

    return next.handle(req);
  }
}

Answer №3

Upon reviewing the code snippet provided, one potential improvement I can suggest is adding a check to verify if there is no value in localStorage before proceeding further. Here's a revised structure for the interceptor:

export class UpdatedAuthInterceptor implements HttpInterceptor {
    
    private APIToken = null;
    private defaultApplicationHeaders = {
        'Content-Type': 'application/json'
    }

    buildRequestHeaders(): HttpHeaders {

        let headers = this.defaultApplicationHeaders;

        // Check and set API-Token if available
        if(this.APIToken !== null) {
            let authHeaderTpl = `Bearer ${this.APIToken}`;
            headers['Authorization'] = authHeaderTpl
        }

        return new HttpHeaders(headers);
    }

    constructor() {
        const tokenFromLocalStorage = localStorage.getItem('id_token');
        if(tokenFromLocalStorage) { // Added check for presence of token in localStorage
            this.APIToken = tokenFromLocalStorage;
        }
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const headers = this.buildRequestHeaders();
        const authReq = req.clone({ headers });

        return next.handle(authReq);
    }
}

Answer №4

For those seeking information on msal-angular's MsalHttpInterceptor, I have developed my own version of the MsalInterceptor.
Below is a snippet of my customized code:

// #region own workaround to avoid specifying every API endpoint URL in settings
if (!scopes && req.url.startsWith(this.settingsService.apiUrl)) {
  scopes = [this.auth.getCurrentConfiguration().auth.clientId];
}
// #endregion

// If no scopes are set for this request, do nothing.
if (!scopes) {
  return next.handle(req);
}

PS: Show your support for using wildcards in protectedResourceMap https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/1776

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 causes error TS2345 to appear when defining directives?

Attempting to transition an existing angular application to typescript (version 1.5.3): Shown below is the code snippet: 'use strict'; angular.module('x') .directive('tabsPane', TabsPane) function TabsPane(ite ...

The Nextjs next-auth implementation with URL '/api/auth/callback/cognito' encountered a 502 Bad Gateway error while in production

I'm facing a challenge while trying to deploy a nextjs app that utilizes 'next-auth' with AWS Cognito. Interestingly, the app runs smoothly when tested locally using either next dev or next start. However, upon deploying it on the producti ...

Display a custom error message containing a string in an Angular error alert

How can I extract a specific string from an error message? I'm trying to retrieve the phrase "Bad Request" from this particular error message "400 - Bad Request URL: put: Message: Http failure response for : 400 Bad Request Details: "Bad Request ...

Why aren't the child elements in my React / Framer Motion animation staggered as expected?

In my finance application, I am creating a balance overview feature. To display the content, I pass props into a single <BalanceEntry> component and then map all entries onto the page. With Framer Motion, my goal is to animate each rendered <Bala ...

The call stack size has reached its maximum limit due to running 2 Collection.find commands

I've encountered a Maximum call stack size exceeded exception in my Chrome console while working with Angular2 and Meteor. This issue started after I added the following code to my client side: var userChannelsId = UserChannels.find({idUser: Meteor.u ...

Enhancing JavaScript functions with type definitions

I have successfully implemented this TypeScript code: import ytdl from 'react-native-ytdl'; type DirectLink = { url: string; headers: any[]; }; type VideoFormat = { itag: number; url: string; width: number; height: number; }; type ...

Angular Pipe displays values properly, but ngFor fails to render them

I am using a pipe to filter my ngFor loop with exact matches that are passed through by clicking on the filter argument. Below is the code for my pipe: transform(values: any[], criteria: string, group): any[] { if (!values) { ...

fusion of Angular and ASP.NET Core

When working with asp.net core and angular, my current process involves: Creating a client app using angular cli Developing an asp.net core mvc app Connecting the two using Microsoft.AspNetCore.SpaServices.Extensions (spa strategy = proxy) Modifying the I ...

Tips for including additional properties to a <button> element using ReactJS and Typescript

Currently, I am in the process of creating a unique component which consists of an ordinary <button> tag and has a new prop called aria-current. The issue at hand is that Typescript is flagging an error stating that this property does not exist with ...

Is there a way to use Lodash to quickly return the same value if a condition is met using the ternary operator

Is there a condensed way to do this using Lodash? (or perhaps Vanilla JS/TypeScript) var val = _.get(obj, 'value', ''); Please note that var val = obj.value || ''; is not suitable as it considers 0 and false as valid but fal ...

Resolving the error message "Default props must have construct or call signatures for 'Component' in Typescript"

I'm currently working on a function component called MyComponent and I'm facing an issue with setting a default prop for component. The goal is to have the root node render as a "span" if no value is provided. However, I am encountering the follo ...

Create categories for static array to enable automatic suggestions

I have a JavaScript library that needs to export various constants for users who are working with vscode or TypeScript. The goal is to enable auto-complete functionality for specific constant options. So far, I've attempted to export a Constant in th ...

"Even after running the 'ng build --prod' command, the updated changes in Angular

When using ng build, all new changes are working fine. However, when I try using ng build --prod in Angular 8, only previous changes get reflected in the dist folder. Can anyone provide assistance with this issue? ...

Using event.target to pass HTML form data to FormData is causing an error stating that the Argument of type 'EventTarget' cannot be assigned to a parameter of type 'HTMLFormElement'

Looking to extract data from a form and store it in FormData: const handleSubmit = (e: FormEvent<HTMLFormElement>) => { e.preventDefault(); const formData = new FormData(e.target as HTMLFormElement); const value = formData.get(' ...

When I interact with a button in a series of buttons, all buttons undergo a change simultaneously rather than just the selected one -Angular

I'm having trouble updating the text inside a button upon clicking In my HTML, I have a list of buttons displaying angular material components: <table *ngIf="buildingImageList.length 0" mat-table [dataSource]="buildingImageList" class="mat-elevat ...

"Exploring the world of TypeScript Classes in combination with Webpack

If I create a TypeScript class with 10 methods and export a new instance of the class as default in one file, then import this class into another file (e.g. a React functional component) and use only one method from the class, how will it affect optimizati ...

Learn how to retrieve images from the web API at 'https://jsonplaceholder.typicode.com/photos' and showcase them on a webpage using Angular10

Using the API "https://jsonplaceholder.typicode.com/photos", I have access to 5 properties: albumId: 1 id: 1 thumbnailUrl: "https://via.placeholder.com/150/92c952" title: "accusamus beatae ad facilis cum similique qui sunt" url: "https://via.placeh ...

Passing a username and password to an API in Angular 6: Step-by-step guide

Can someone assist me with this problem? I am struggling to pass the username and password in my API request for a list of homes. Every attempt I've made has resulted in an unauthorized access error. How do I correctly include the user credentials (pe ...

Intellij2016 is issuing warnings for the sass use of the :host selector in Angular2 code

One interesting feature of Angular 2 is the special selector it uses to reference its own component: :host display: block height: 100% !important width: 100% text-align: center position: relative However, Intellij does not provide a way to supp ...

Using Angular Material theme with Webpack

In the process of configuring angular material for my Angular (4) application using webpack, I discovered that including a default theme is necessary for it to function properly. One of the recommendations provided in the documentation is to use @import & ...