import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse, AxiosError, } from 'axios'; import type { UserCredentials } from 'react-native-keychain'; import { type B2CConfiguration, TasmuB2CClient } from '../auth/Tasmub2cClient'; import SecureStoreManager from '../SecureStoreManager'; import StoreManager from '../StorageManager'; import { apiConfigurationTEST, b2cConfigTEST, b2cScopesTEST, } from './environments/test'; import { apiConfigurationDEV, b2cConfigDEV, b2cScopesDEV, } from './environments/dev'; import { apiConfigurationPREPROD, b2cConfigPREPROD, b2cScopesPREPROD, } from './environments/preprod'; import { apiConfigurationPROD, b2cConfigPROD, b2cScopesPROD, } from './environments/prod'; import { apiConfigurationUAT, b2cConfigUAT, b2cScopesUAT, } from './environments/uat'; import { Environment } from '../../utils/enums'; export interface ConnectionConfig { baseURL: string; headers?: Record; timeout?: number; } const getConfigs = (environment: string) => { switch (environment) { case Environment.TEST: return { apiConfiguration: apiConfigurationTEST, b2cConfig: b2cConfigTEST, b2cScopes: b2cScopesTEST, }; case Environment.DEV: return { apiConfiguration: apiConfigurationDEV, b2cConfig: b2cConfigDEV, b2cScopes: b2cScopesDEV, }; case Environment.PROD: return { apiConfiguration: apiConfigurationPROD, b2cConfig: b2cConfigPROD, b2cScopes: b2cScopesPROD, }; case Environment.PREPROD: return { apiConfiguration: apiConfigurationPREPROD, b2cConfig: b2cConfigPREPROD, b2cScopes: b2cScopesPREPROD, }; case Environment.UAT: return { apiConfiguration: apiConfigurationUAT, b2cConfig: b2cConfigUAT, b2cScopes: b2cScopesUAT, }; default: return { apiConfiguration: apiConfigurationPROD, b2cConfig: b2cConfigPROD, b2cScopes: b2cScopesPROD, }; } }; /** * @param b2cConfig b2c configuration object * @param b2cScopes b2c scopes * @returns saved new refresh token in secure store manager * with new token expiry time. */ const getNewToken = async ( b2cConfig: B2CConfiguration, b2cScopes: string[] ) => { const b2cClient = new TasmuB2CClient(b2cConfig); await b2cClient.init(); const res = await b2cClient.acquireTokenSilent({ scopes: b2cScopes, forceRefresh: true, }); return res; }; export const getNewRefreshedToken = async (environment: any) => { const configs = getConfigs(environment); await getNewToken(configs.b2cConfig, configs.b2cScopes); }; /** * Function to check token expiry. * @returns boolean true if expired. */ const checkTokenExpiry = async () => { const tStamp = (await StoreManager.getStoredItem('timestamp')) as string; return Date.now() >= JSON.parse(JSON.parse(tStamp)) * 1000; }; export default class APIHandler { private client: AxiosInstance; protected createAxiosClient(config: ConnectionConfig): AxiosInstance { const http = axios.create(config); return http; } static sharedInstance: APIHandler; static initialize(environment: any): APIHandler { SecureStoreManager.environment = environment; APIHandler.sharedInstance = new APIHandler(environment); return APIHandler.sharedInstance; } constructor(environment: any) { const configs = getConfigs(environment); this.client = this.createAxiosClient(configs.apiConfiguration); this.client.interceptors.request.use( async (request) => { try { const isTokenExist = (await SecureStoreManager.retrieveItem( 'Auth' )) as false | UserCredentials; if (isTokenExist) { // check if token is valid const isTokenExpired = await checkTokenExpiry(); if (isTokenExpired) { // Refresh Token await getNewToken(configs.b2cConfig, configs.b2cScopes); const refreshedToken = (await SecureStoreManager.retrieveItem( 'Auth' )) as false | UserCredentials; if (refreshedToken && request.headers) { request.headers.Authorization = `Bearer ${refreshedToken.password}`; } } else { // Token is valid if (request.headers) { request.headers.Authorization = `Bearer ${isTokenExist.password}`; } } } return request; } catch (error) { return request; } }, (error) => Promise.reject(error) ); this.client.interceptors.response.use( (response) => response, (error) => { const { response } = error; return this.handleError(response); } ); } get>( url: string, config?: AxiosRequestConfig ): Promise { return this.client.get(url, config); } post>( url: string, data?: T, config?: AxiosRequestConfig ): Promise { return this.client.post(url, data, config); } put>( url: string, data?: T, config?: AxiosRequestConfig ): Promise { return this.client.put(url, data, config); } patch>( url: string, data?: T, config?: AxiosRequestConfig ): Promise { return this.client.patch(url, data, config); } delete>( url: string, config?: AxiosRequestConfig ): Promise { return this.client.delete(url, config); } private handleError(error: AxiosError) { return Promise.reject(error); } }