Created
September 21, 2017 11:34
-
-
Save kyawkyawsoezhu/3758173c37e16cedc84313b88d9f15ff to your computer and use it in GitHub Desktop.
Redux middleware to request token and put into API endpoint header
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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