import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from 'axios'; import { WithMicroserviceContext } from '../with-microservice'; import { AuthAdapter } from './auth-adapter'; export interface BearerTokenAuthOptions {} export class BearerTokenAuth implements AuthAdapter { private authAbortController?: AbortController; private authToken: string = ''; private authURL: string; private axios: AxiosInstance; constructor(context: WithMicroserviceContext, _options?: BearerTokenAuthOptions) { this.authURL = context.authURL ?? `${context.baseURL}/auth`; this.axios = axios.create(); } onDestroy() { this.abortAuthRequest(); } authenticate() { this.authAbortController = new AbortController(); return this.axios .get(this.authURL, { signal: this.authAbortController.signal, }) .then(response => { this.authToken = response.data; }) .catch((error: AxiosError) => { this.authToken = ''; return Promise.reject(error); }) .finally(() => { this.authAbortController = undefined; }); } interceptRequest(config: AxiosRequestConfig) { config.headers = { ...config.headers, ...this.getAuthHeader(), }; } didAuthenticationHappenWhileRequestInFlight(config: AxiosRequestConfig) { /** * Check if auth token got updated while the request was in flight to avoid unnecessary extra auth request * For example, if two requests requiring authentication happen at the same time * - the first takes 5 seconds to get a 401 response, then runs authenticate and sets the auth token * - the second takes 10 seconds to get a 401 response, then we don't want to do an auth request because we * already have an auth token */ return ( this.getAuthHeader().Authorization !== config.headers?.Authorization && this.authToken !== '' ); } abortAuthRequest() { if (this.authAbortController) { this.authAbortController.abort(); this.authAbortController = undefined; } } getAuthHeader() { return { Authorization: `Bearer ${this.authToken}`, }; } }