import type { AxiosRequestConfig } from 'axios' import createAuthRefreshInterceptor from 'axios-auth-refresh' import { AuthRefreshFunc, Tokens } from '../models' import { AxiosService } from './axios' import { GuardRequestConfig } from './guard' import { v4 as uuidv4 } from 'uuid' export class APIService extends AxiosService { private authRefreshFn?: AuthRefreshFunc private tokens: Tokens = {} /** * The API Service lets you use an axios API and handles oro backend services authentification via JWT tokens * @param useLocalStorage if set to true, tokens will be stored in localStorage * @param config (optional) an axios config * @param tokenRefreshFailureCallback (optional) callback to call when failing to refresh the auth token */ constructor( private useLocalStorage: boolean, config?: AxiosRequestConfig, private tokenRefreshFailureCallback?: (err: Error) => void ) { super(config) const self = this const sessionId = uuidv4() this.axios.interceptors.request.use( (config) => { const token = (config as GuardRequestConfig).useRefreshToken ? self.getTokens().refreshToken : self.getTokens().accessToken config.headers = { ...config.headers, Authorization: `Bearer ${token}`, 'X-Session-Id': sessionId, 'X-Request-Id': uuidv4(), } return config }, (error) => { Promise.reject(error) } ) createAuthRefreshInterceptor( this.axios, async function (failedRequest) { if (self.authRefreshFn) { try { let tokenResp = await self.authRefreshFn(self.getTokens().refreshToken) self.setTokens({ accessToken: tokenResp.accessToken, refreshToken: tokenResp.refreshToken, }) failedRequest.response.config.headers['Authorization'] = `Bearer ${ self.getTokens().accessToken }` return Promise.resolve() } catch (e) { console.error('an error occured while refreshing tokens (notifying callback)', e) if (self.tokenRefreshFailureCallback) self.tokenRefreshFailureCallback(failedRequest) return Promise.resolve() // We keep it like that. Otherwise, it seems to break the api service will it is not needed // return Promise.reject(e) } } console.error('The request could not refresh the token (authRefreshFn was not set)', failedRequest) return Promise.resolve() // We keep it like that. Otherwise, it seems to break the api service will it is not needed // return Promise.reject(failedRequest) }, { statusCodes: [401, 403] } ) } public setAuthRefreshFn(fn: AuthRefreshFunc) { this.authRefreshFn = fn } public setTokens(tokens: Tokens) { if (this.useLocalStorage) { localStorage.setItem('tokens', JSON.stringify(tokens)) } this.tokens = tokens } public getTokens(): Tokens { if (this.useLocalStorage) { let tokens: Tokens = {} const item = localStorage.getItem('tokens') if (item) { tokens = JSON.parse(item) } return tokens } else { return this.tokens } } }