The initial invocation of OidcSecurityService.getAccessToken() returns null as the token

Our internal application requires all users to be authenticated and authorized, including for the home page. To achieve this, we use an HttpInterceptor to add a bearer token to our API requests.

Initially, when rendering the first set of data with the first API call, the token is null. The token remains null even after receiving it from the getAccessToken() subscription. However, subsequent API calls work fine, even if the user refreshes the page (F5).

We are currently using angular 16.1 along with [email protected]. This issue arose when we upgraded from a previous version of angular-auth-oidc-client that did not utilize rxjs subscribe.

import { Injectable, Injector } from "@angular/core";
import { Observable } from "rxjs";
import {
  HttpInterceptor,
  HttpEvent,
  HttpRequest,
  HttpHandler,
} from "@angular/common/http";
import { OidcSecurityService } from "angular-auth-oidc-client";
import { Router } from "@angular/router";
import { TokenValidatorService } from "../../services/token-validator.service";

export const UseOidcInterceptorHeader = "X-Oidc-Interceptor";

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  token = null;
  constructor(
    public oidcSecurityService: OidcSecurityService,
    private tokenValidatorService: TokenValidatorService,
    private router: Router
  ) {
    this.oidcSecurityService.getAccessToken().subscribe((token) => {
      this.token = token
    });
  }

  intercept(
    req: HttpRequest<unknown>,
    next: HttpHandler
  ): Observable<HttpEvent<unknown>> {
    // we do not intercept authentication calls to "companyAuthServer"
    if (req.url.includes("companyAuthServer")) {
      return next.handle(req);
    }

    if (this.token !== null) {
      if (!this.tokenValidatorService.checkUserIsInAdGroup(this.token)) {
        this.router.navigate(["/oidc/unauthorized"]);
      }

      const requestForward = req.clone({
        setHeaders: { Authorization: "Bearer " + this.token },
      });

      return next.handle(requestForward);
    } else {
      console.error(
        "OidcSecurityService, token not fetched: NO authorize header!",
        req.urlWithParams
      );
    }
    return next.handle(req);
  }
}

Answer №1

Asynchronous results in the constructor can cause issues if not handled correctly. When you subscribe and store the async result in this.token, it won't be set by the time intercept() is first called.

To avoid this problem, you can return an observable that waits for the async call to complete before proceeding with the next request:

intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
  if (req.url.includes("companyAuthServer")) {
    return next.handle(req);
  }

  return this.oidcSecurityService.getAccessToken().pipe(
    tap(token => {
      if(!token || !this.tokenValidatorService.checkUserIsInAdGroup(token)) {
        this.router.navigate(["/oidc/unauthorized"]);
      }
    }),
    map(token => req.clone({ setHeaders: { Authorization: `Bearer ${token}` }}),
    switchMap(authRequest => next.handle(authRequest))
  );
}

Please note that this code snippet has not been tested, so some adjustments may be necessary. However, it should provide a good starting point for your implementation.

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

Implement adminLTE into a new Angular2 project

I tried to integrate the adminlte template into my Angular project 2. I added the adminlte folder to the node_modules, but when linking the .js and .css files in the angular index.html, they couldn't be found. I'm unsure of the reason behind this ...

How can a date range validation be implemented with two distinct ngx date picker fields in Angular 10?

Click here to view the image description In the illustration provided, I am seeking a solution to restrict users from selecting dates in the end date picker that occur before the chosen start date. ...

Exploring the synergies between Typescript unions and primitive data types in

Given the scenario presented interface fooInterface { bar: any; } function(value: fooInterface | string) { value.bar } An issue arises with the message: Property 'bar' does not exist on type '(fooInterface | string)' I seem ...

Is there a way to create a stub for the given function using sinon?

Here is my test function: const customTestLibrary = require("./test"); describe("Initial Test", function() { it("should run the test function successfully", function(done) { customTestLibrary.execute(); done(); }); }); The te ...

Automatically convert user input into a currency format as they type in the input field

I have a requirement for a text field where the user's input should be formatted as follows when they start entering: 0.00 (Start) 0.01 0.12 1.23 12.34 123.45 1,234.56 12,345.67 123,456.78 I am currently using a currency mask which works well, but I ...

Using typescript with create-react-app - organizing types in a separate file

I'm currently developing a project using Create React App with TypeScript (create-react-app myapp --typescript) In my App.tsx file, I have written some TypeScript code that I want to move to an external file. I have tried creating App.d.ts, index.d.t ...

Difficulty with two-dimensional arrays in Angular and Typescript

I am currently stuck trying to assign values to a 2-dimensional object array in Angular/Typescript. I have noticed that the last assignment seems to override the previous ones, but I cannot pinpoint why this is happening. Could someone please review my cod ...

Discover a more efficient method for expanding multiple interfaces

Hey there, I'm having some trouble with TypeScript and generics. Is there a better way to structure the following code for optimal cleanliness and efficiency? export interface Fruit { colour: string; age: number; edible: boolean; } export inte ...

There seems to be an issue with the request header field Authorization, as it is not permitted by the Access-Control-Allow-Headers in

My project involves using Angular 2 for the client side and Laravel 5.4 for the server side. I have set up APIs in Laravel and am making requests to them from Angular 2. In Laravel, I have configured Oauth 2 passport service to authenticate all APIs. For ...

NgFor is designed to bind only to Iterables like Arrays

After exploring other questions related to the same error, I realized that my approach for retrieving data is unique. I am trying to fetch data from an API and display it on the page using Angular. The http request will return an array of projects. Below ...

Setting up an auto-complete dropdown with PrimeNG and Angular

Struggling with the auto-complete feature, where the dropdown loads initially but fails to filter values as I type. Here's a snippet from service.ts: getUserLocations(UserID: string, allList:string ) { return this._http.get(environment.BASE ...

There are a few steps to take in order to sort an array of objects by a

I want to organize and display data in a sortable table format javascript let dataList = [ { idx: number, name: string, btn: number, index: number }, { idx: number, name: string, btn: number, index: number }, { idx: number, name: string, btn: number ...

Learn how to set expectations on the object returned from a spied method, Jasmine

I am currently faced with setting an expectation regarding the number of times a specific function called "upsertDocument" is executed. This function is part of a DocumentClient object within a getClient method in production. Here's how it looks: cli ...

The Google Books API has reached its limit for requests

Encountering a rate limit exceeded error from the Google Books API while using this demo: To reproduce, open the developer console in Chrome and perform some searches. The rate limit errors will be displayed in the console. [],"lazyUpdate":null},"status" ...

The selection elements fail to reset correctly

I'm currently working on an Angular 4 application where I have a form that includes a select element inside a box. <div class="form-group"> <label for="designation">Designation</label> <select [class.red-borde ...

What is the process for sending data to the backend using an HTTP POST request? The frontend is built with Angular 2 and the backend is developed with .NET

Having trouble with this error message: Error: XMLHttpRequest cannot load "http://localhost:49873/api/home". The response to the preflight request failed the access control check. There is no 'Access-Control-Allow-Origin' header present on the ...

Guide on incorporating file uploads in an Angular 6 project

I am currently working on creating a component where I have implemented a file upload function in a child component and am attempting to use that component's selector in another one. Here is my project structure: - upload : upload.component.html up ...

Is it feasible to trigger a selector element in Angular 2 upon clicking another element?

I want to trigger the Angular2 Material Datepicker's calendar popup by clicking on another element on the page. More specifically: <material-datepicker> </material-datepicker> should be triggered when a specific text is clicked: <p&g ...

Error message: Typescript class unable to access methods because of undefined properties (TypeError: Cannot read properties of undefined (reading 'method_name'))

During the compilation process, everything goes smoothly with no errors. However, when I try to call the method in the controller, an error occurs. It seems that some class methods are missing during the compilation phase. If you require more information ...

Encountering an Error in Angular Material 8 due to the Import of the Unforeseen Directive 'MatCard'

I keep encountering the error message displayed above in the console. I am currently working with Angular Material version 8.2.3. In my app.module.ts file, I have the following import statements related to Angular Material: import { MatInputModule, MatBu ...