Skip to content

Instantly share code, notes, and snippets.

@kyawkyawsoezhu
Created September 21, 2017 11:34
Show Gist options
  • Save kyawkyawsoezhu/1a5ed8360d834bccc5c6f5016ec13a8b to your computer and use it in GitHub Desktop.
Save kyawkyawsoezhu/1a5ed8360d834bccc5c6f5016ec13a8b to your computer and use it in GitHub Desktop.
Redux middleware to request token and put into API endpoint header
import jwt from 'jsonwebtoken';
import moment from 'moment';
import axios from '../tools/axios';
const isTokenAboutToExpire = (token) => {
const tokenPayload = jwt.decode(token.access_token);
const expiry = moment.unix(tokenPayload.exp);
return expiry.diff(moment(), 'seconds') < 300;
};
const retrieveTokenFromLocal = (key) => {
const storedValue = localStorage.getItem(key);
if (storedValue) {
try {
return JSON.parse(localStorage.getItem(key));
} catch (e) {
if (e instanceof SyntaxError) {
return null;
}
throw e;
}
}
return false;
};
const saveToken = (key, token) => {
localStorage.setItem(key, JSON.stringify(token));
};
const requestNewToken = ({ accessTokenURL, grantType, clientID, clientSecret }) => (
axios({
url: accessTokenURL,
method: 'POST',
headers: {
Accept: 'application/json',
},
data: {
grant_type: grantType,
client_id: clientID,
client_secret: clientSecret,
},
})
);
const shouldRequestNewToken = (tokenStorageKey) => {
const token = retrieveTokenFromLocal(tokenStorageKey);
return token ? isTokenAboutToExpire(token) : true;
};
const continuesActions = ({ actions, accessToken, next }) => {
actions.map((action) => {
const payload = axios(Object.assign({}, action.payload, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
}));
const newAction = Object.assign({}, action, { payload });
next(newAction);
});
};
const fetchAPIToken = config => (
new Promise((resolve, reject) => {
if (shouldRequestNewToken(config.tokenStorageKey)) {
requestNewToken(config).then((response) => {
const token = response.data;
saveToken(config.tokenStorageKey, token);
return resolve(token);
}).catch(reason => (reject(reason)));
} else {
return resolve(retrieveTokenFromLocal(config.tokenStorageKey));
}
})
);
let actions = [];
let isFetching = false; // avoid requesting token multiple time
export default config => store => next => action => {
if (!action.needToken) {
return next(action);
}
// keep all needed token actions to run after receive token
actions.push(action);
if (!isFetching) {
isFetching = true;
fetchAPIToken(config).then((token) => {
const accessToken = token.access_token;
continuesActions({ actions, accessToken, next });
actions = [];
isFetching = false;
}).catch((reason) => {
console.error(reason);
});
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment