Skip to content

Instantly share code, notes, and snippets.

@jackfiallos
Created March 27, 2021 14:32
Show Gist options
  • Save jackfiallos/6ac4d8caeb6cea932348b4c1222b0904 to your computer and use it in GitHub Desktop.
Save jackfiallos/6ac4d8caeb6cea932348b4c1222b0904 to your computer and use it in GitHub Desktop.
React Native token, refresh token and OTP handling options
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { API_URL } from 'react-native-dotenv';
import { IError } from '../interfaces/generic';
import { ISession } from '../interfaces/network';
// these are methods to store, remove or get the token from the localstorage
import { getCredentials, performLogin, performLogout } from './auth';
import NavigationService from './NavigationService';
let isRefreshing = false;
let refreshSubscribers: ((token: string) => void)[] = [];
// This is used to store requests after the refresh token is performed
// and will be used to trigger the request with the new token
const subscribeTokenRefresh = (cb: (token: string) => void) => refreshSubscribers.push(cb);
const onRefreshed = (token: string) => refreshSubscribers.forEach(cb => cb(token));
const API: AxiosInstance = axios.create({
baseURL: API_URL,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
headers: {
'Content-Type': 'application/json',
},
});
API.interceptors.request.use(
(config: AxiosRequestConfig) =>
getCredentials().then((credentials: string | null) => {
const userCredentials = credentials && credentials.length ? JSON.parse(credentials) as ISession : undefined;
const token = userCredentials?.token;
if (token) {
config.headers.Authorization = `JWT ${token}`;
}
return Promise.resolve(config);
}),
() => {
// TODO:
}
);
API.interceptors.response.use(
(response) => {
return response;
},
async (error: AxiosError) => {
// save original request
const originalRequest = error.config;
// act when refresh token enabled in the api
if (error.response?.status === 498) {
if (!isRefreshing) {
isRefreshing = true;
const storedCredentials = await getCredentials();
const credentials = storedCredentials && storedCredentials.length ? JSON.parse(storedCredentials) as ISession : undefined;
if (credentials) {
const { token, refresh_token } = credentials;
axios
.post(`${API_URL}/refresh-token`, {token, refresh_token})
.then(({ data: newCredentials }: AxiosResponse<ISession>) => {
performLogin(newCredentials);
onRefreshed(newCredentials.token);
})
.catch(() => {
performLogout();
NavigationService.navigate('Landing', {});
})
.finally(() => {
isRefreshing = false;
refreshSubscribers = [];
});
}
}
return new Promise((resolve) => {
subscribeTokenRefresh((token: string): void => {
// replace token
originalRequest.headers['Authorization'] = `JWT ${token}`;
return resolve(axios(originalRequest));
});
});
}
// act when the OTP security step is enabled in the api
if (error.response?.status === 499) {
// Response for OTP login
return Promise.resolve(error.response);
}
let apiError: IError = {
message: 'Ooops! Something is wrong or your user is not authorized',
};
if (error.response && error.response.data && error.response.data.message && error.response.status !== 401) {
apiError = {
...apiError,
message: error.response.data.message,
request_id: error.response.data.request_id || undefined,
details: error.response.data.details && error.response.data.details.length ? error.response.data.details : [],
};
}
if (error.response && error.response.status === 401) {
// maybe the token is expired, so, try to remove the token anyway
performLogout();
NavigationService.navigate('Landing', {});
}
return Promise.reject(apiError);
}
);
export default API;
// export interface ISession {
// token: string;
// refresh_token: string;
// otp: boolean;
// hash?: string;
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment