Skip to content

Instantly share code, notes, and snippets.

@manuelbieh
Created August 2, 2019 09:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save manuelbieh/81fa5d3cbcc41cfee227af3b146f45bc to your computer and use it in GitHub Desktop.
Save manuelbieh/81fa5d3cbcc41cfee227af3b146f45bc to your computer and use it in GitHub Desktop.
import axios from 'axios';
import applyConverters from 'axios-case-converter';
import { objectKeysToCamelCase } from 'utils';
export const redirectToLogin = () => {
window.location.href = process.env.AUTH_URL;
};
export const getAccessToken = () =>
(JSON.parse(window.localStorage.getItem('auth')) || {}).accessToken;
export const getRefreshToken = () =>
(JSON.parse(window.localStorage.getItem('auth')) || {}).refreshToken;
// We assume a user is loggedin if he has an AccessToken, no matter if it is valid or not
export const isLoggedIn = () => Boolean(getAccessToken());
export const isUnauthorized = (error) =>
error && error.response && error.response.status === 401;
export const storeAuthObject = (authObject) => {
window.localStorage.setItem(
'auth',
JSON.stringify(objectKeysToCamelCase(authObject))
);
};
export const getAccessTokenUsingAccessCode = async (accessCode) => {
try {
const request = axios.create({ baseURL: process.env.API_HOST });
const token = await request.get('/auth/login', { params: { accessCode } });
storeAuthObject(token.data);
return true;
} catch (err) {
return false;
}
};
export const getAccessTokenUsingRefreshToken = async (
refreshToken = getRefreshToken()
) => {
try {
const request = axios.create({ baseURL: process.env.API_HOST });
const token = await request.get('/auth/refresh', { params: { refreshToken } });
storeAuthObject(token.data);
} catch (err) {
if (isUnauthorized(err)) {
redirectToLogin();
}
}
};
export const Request = (baseURL = process.env.API_HOST) => {
return applyConverters(axios.create({ baseURL }));
};
// We're wrapping Axios' http methods here so Axios tries to retrieve a fresh access token using
// a possibly existing refresh token first before redirecting to the keycloak login
export const wrapAuthenticatedRequestMethod = (client, verb) => ({
[verb]: (...args) => {
const makeRequest = async (wasRetried = false) => {
try {
const response = await client[verb](...args);
return response.data;
} catch (err) {
if (isUnauthorized(err) && wasRetried === false) {
await getAccessTokenUsingRefreshToken();
return makeRequest(true);
}
if (wasRetried === true) {
redirectToLogin();
}
}
};
return makeRequest();
},
});
export const AuthenticatedRequest = (baseURL = process.env.API_HOST) => {
const client = Request(baseURL);
client.interceptors.request.use((config) => {
const accessToken = getAccessToken();
config.headers.common.Authorization = `Bearer ${accessToken || ''}`;
return config;
});
return {
...wrapAuthenticatedRequestMethod(client, 'get'),
...wrapAuthenticatedRequestMethod(client, 'post'),
...wrapAuthenticatedRequestMethod(client, 'patch'),
...wrapAuthenticatedRequestMethod(client, 'put'),
...wrapAuthenticatedRequestMethod(client, 'delete'),
};
};
export const wrapRequestMethod = (client, verb) => ({
[verb]: async (...args) => {
const response = await client[verb](...args);
return response.data;
},
});
export const wrapRequestMethods = (client, methods = []) =>
methods.reduce((wrapped, method) => {
return { ...wrapped, ...wrapRequestMethod(client, method) };
}, {});
export const API = (client = Request()) =>
wrapRequestMethods(client, ['get', 'post', 'patch', 'put', 'delete']);
export default API;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment