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

The function validation_1.validateName in Ionic/Angular is not recognized as a valid function, causing an error that prevents

My development environment includes Ionic Angular. After upgrading from Angular 9 to Angular 14 and Ionic 4 to Ionic 5, I encountered an issue where I can no longer generate pages or components using the command: ionic g page [PATH] The process now trigge ...

Troubleshooting: Fixing the "@angular/core" module not found error in Visual Studio 2019

As I embark on my Asp.Net Core Project with Angular template, I encounter an issue with every import row that includes '@angular/...'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-bro ...

Redirecting to login on browser refresh in Angular using Firebase's canActivate feature

An Angular 5 authentication application using angularfire2 and Firebase has been developed. The app functions correctly when navigating through in-app links. However, an issue arises when refreshing the browser, as it redirects back to the Login page even ...

tips for concealing a row in the mui data grid

I am working on a data grid using MUI and I have a specific requirement to hide certain rows based on a condition in one of the columns. The issue is that while there are props available for hiding columns, such as hide there doesn't seem to be an eq ...

Angular router for displaying multiple view groups

What is the most effective strategy for handling two groups of views in Angular? Let me explain how I typically structure my layout in app.component.html: <app-header></app-header> <router-outlet></router-outlet> <app-footer> ...

What is the correct way to invoke a function from a separate file in typescript?

I am new to typescript and still learning. I have a question regarding calling a function defined in file B from file A. Can someone guide me on how to achieve this? ...

Every key must be a string or number; received a function instead

I encountered a peculiar situation while working with Cucumber: Scenario Outline: Protractor and Cucumber Test InValid Given I have already...... When I fill the <number> .... Examples: | number|... | 3 |... |4 |... Moreover, I ...

Handling functions in Ant Design Select component with TypeScript types

I have a query. Antd offers a custom Select input with functions like onSelect, onChange, etc. I am utilizing the onSelect function which requires the following arguments: (JSX attribute) onSelect?: ((value: string | number | LabeledValue, option: OptionDa ...

Guide to creating unit tests using Mocha, JsDom, and Angular

I am currently working on setting up a unit test project using Mocha and Angular. In order to execute it in the console with jUnit report for Jenkins, I have included JsDom in my project. However, I am facing an issue with loading my Angular application. ...

Angular - Reacting to dynamically rendered content post-navigation

I am working on an angular application that consists of various components and pages where users can create and view html content. Once the content is fully loaded, I need to search for specific elements and add an onclick event to them. My initial approa ...

Necessary Typescript class property when executing code

Is there a way to determine if a class property is required in Typescript at runtime? export class A { public readonly ab?: number; public readonly ac?: number; public readonly ad: number; public readonly ae: number; } Can emitDecoratorMetadata o ...

Using TypeScript to define values with the placeholder "%s" while inputting an object as a parameter

One common way to decorate strings is by using placeholders: let name = "Bob"; console.log("Hello, %s.", name) // => Outputs: "Hello, Bob." I'm curious if there's a way to access specific values within an object being passed in without specif ...

The art of creating an asynchronous function: A comprehensive guide

My goal is to download files from a Firebase bucket and then store them in a database. I need the download process to be asynchronous, ensuring that each file is fully downloaded and added to an array before moving on to the next one. However, my current ...

Is there a way to create a type interface that points to the structure of another type?

I am focused on developing a type interface that includes properties defined in another interface. Below is the schema definition for a model object in TypeScript: export interface ModelSchema { idAttribute: string; name: string; attributes: { ...

The Exporting menu in AmCharts4 does not include the ability to export in CSV, XLSX, or JSON formats

Right now, I am working with AmCharts4 and my objective is to export chart data in various formats such as CSV, XLSX, and JSON. To implement this, I have included the following scripts in index.html: <script src="https://www.amcharts.com/lib/4/core.js" ...

What could be causing the vue-property-decorator @Emit to malfunction in my Vue TypeScript file?

I am currently working with Typescript and Vuejs, where I have a child component called child.component.tsx import Vue from 'vue'; import Component from 'vue-class-component'; import { Emit } from 'vue-property-decorator'; ...

What is the best way to retrieve data (using GET) following React state changes?

Whenever a user clicks on one of the orderBy buttons (such as name/email/date), a new rendered result should be fetched from the server by sending a new get request. The same applies to page pagination. Simply setting this.setState({ [thestate]: [newState ...

What is the process for deploying a .NET Core + Angular single page application to multiple environments?

After utilizing the Angular project template with ASP.NET Core in Visual Studio 2019, I developed an application that includes multiple environments: DEV, STG, and Production. Each environment corresponds to specific "appsettings.json" files for the .NET p ...

Using FIND to search an array object results in an error: Type 'undefined' is not compatible with type ''

I'm currently attempting to search for an element within one array and then assign it to another array object. However, I keep receiving the following error message: Type 'ClosureSummary | undefined' is not assignable to type 'Closure ...

Is Angular 4 failing to set headers properly or is Express.js searching in the wrong place?

When interacting with an Express.js API, I encountered a issue regarding the handling of auth tokens. The problem arose when sending the token in the request headers using Angular 4 compared to Postman. In Postman, setting the header named 'Authorizat ...