I am trying to build an Angular 17 standalone app that can perform a GET request to my Flask REST API backend.
The Angular app has been set up with authentication using MSAL and is functioning with the redirect method for authentication.
Now I am trying to use the MsalInterceptor to append an access token to http.get
request to the sites listed in the protectedResourceMap.
I have a class called AuthFactories where the snippet below is from.
auth.factories.ts
public MSALInterceptorConfigFactory(config: AppConfigService): MsalInterceptorConfiguration {
console.log("MSALInterceptorConfigFactory");
const protectedResourceMap = new Map<string, Array<string>>();
const apiUrl = config.getConfig("apiUrl");
const clientId = config.getConfig("clientId");
protectedResourceMap.set(`/`, [
`${clientId}/Flask.Access`,
]);
console.log("Protected Resource Map:", protectedResourceMap);
return {
interactionType: InteractionType.Redirect,
protectedResourceMap: protectedResourceMap
};
}
This is then added to the bootstrapApplication providers as below.
main.ts code
{
provide: MSAL_INTERCEPTOR_CONFIG,
useFactory: authFactories.MSALInterceptorConfigFactory,
deps: [AppConfigService]
}
I believe the providers and protectedResourceMap are set up correctly, because the header is added when I run my http.get()
command from in the component called db.components.ts
. However, if I put the same call in a function within db.service.ts
and then call that it fails to be intercepted. What changes are required to service.ts
for the HTTP request to be successfully intercepted so the authorisation token can be added to the header?
db.components.ts code
import { Component, OnInit } from '@angular/core';
import { DBService } from '~/services/db.service';
import { HttpClient } from '@angular/common/http';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-db-cnxn',
standalone: true,
imports: [CommonModule],
templateUrl: './db-cnxn.component.html',
styleUrls: ['./db-cnxn.component.css']
})
export class DbCnxnComponent implements OnInit {
public responseData: any;
public errorMessage: string | null = null;
constructor(
private dbService: DBService,
private http: HttpClient
) {}
ngOnInit(): void {
this.testConnection();
}
public async testConnection(): Promise<void> {
try {
this.responseData = await this.dbService.testConnection(); // VERSION A. This call does not get intercepted so no Auth token is added to the header
this.responseData = this.dbService.testConnection();
this.http.get("http://127.0.0.1:5000/health")
.subscribe(responseData => {
this.responseData = responseData;
}); // VERSION B. This call is successfully intercepted and the Auth token is added to the header
console.log("Flask Backend Connection Successful");
} catch (error) {
this.errorMessage = 'Failed to connect to the database. Please try again later.';
console.error(error);
}
}
}
db.service.ts code
import { Injectable } from '@angular/core';
import { HttpClient, HttpBackend } from '@angular/common/http';
import { lastValueFrom } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DBService {
private apiUrl = 'http://127.0.0.1:5000/health';
private http: HttpClient;
constructor(private httpHandler: HttpBackend) {
this.http = new HttpClient(this.httpHandler);
}
public async testConnection(): Promise<any> {
const p = new Promise<any>((resolve, reject) => {
try {
console.log("Getting API"); // DEBUG COMMENT
let stream$ = this.http.get(this.apiUrl); // The MSALInterceptorConfigFactory should add header tokens
lastValueFrom(stream$).then((data: any) => {
resolve(data);
});
} catch(error: any) {
reject(error);
}
});
return await p;
}
}