diff --git a/src/lib/createAuthContext.js b/src/lib/createAuthContext.js index a2bfbb3..b0a8efc 100644 --- a/src/lib/createAuthContext.js +++ b/src/lib/createAuthContext.js @@ -5,6 +5,7 @@ import { fetchToken } from './helpers/fetchToken' import { removeCodeFromLocation } from './helpers/removeCodeFromLocation' import { getVerifierFromStorage } from './helpers/getVerifierFromStorage' import { removeVerifierFromStorage } from './helpers/removeVerifierFromStorage' +import {exchangeRefreshForAccessToken} from './helpers/exchangeRefreshForAccessToken' export default ({ clientId, @@ -39,10 +40,24 @@ export default ({ } const useToken = () => { - const { token } = useContext(context) - if (!token) { - console.warn(`Trying to useToken() while not being authenticated.\nMake sure to useToken() only inside of an component.`) - } + const { token, setToken } = useContext(context) + useEffect(() => { + if (token) { + const now = new Date() + const elapsed = new Date(token.expires_at).getTime() - now.getTime() + const slack = 10000 + if(token.refresh_token) { + const timer = setTimeout(() =>exchangeRefreshForAccessToken({clientId, clientSecret, tokenEndpoint, fetch , token }) + .then(response => { + setToken(response) + }),elapsed - slack ) + return () => clearTimeout(timer) + } + } else { + console.warn(`Trying to useToken() while not being authenticated.\nMake sure to useToken() only inside of an component.`) + } + },[token]) + return token } @@ -56,6 +71,7 @@ export default ({ if (!token) { const code = getCodeFromLocation({ location: window.location }) const verifier = getVerifierFromStorage({ clientId, storage }) + const slackSeconds = 10 if (code && verifier) { fetchToken({ clientId, clientSecret, tokenEndpoint, code, verifier, fetch @@ -63,7 +79,7 @@ export default ({ .then(setToken) .then(() => { removeCodeFromLocation() - removeVerifierFromStorage({ clientId, storage }) + removeVerifierFromStorage({ clientId, storage }) }) .catch(e => { console.error(e) @@ -81,7 +97,7 @@ export default ({ } return ( - + {children} ) diff --git a/src/lib/helpers/exchangeRefreshForAccessToken.js b/src/lib/helpers/exchangeRefreshForAccessToken.js new file mode 100644 index 0000000..601f1a7 --- /dev/null +++ b/src/lib/helpers/exchangeRefreshForAccessToken.js @@ -0,0 +1,35 @@ +export const exchangeRefreshForAccessToken = ({ clientId, clientSecret, tokenEndpoint, fetch = window.fetch, token }) => { + const payload = { + client_secret: clientSecret, + client_id: clientId, + grant_type: "refresh_token", + scope: "openid, profile", + refresh_token: token.refresh_token + }; + return fetch(tokenEndpoint, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + method: 'POST', + body: new window.URLSearchParams(payload) + }) + .then(r => { + if (!r.ok) { + throw new Error(`Token response not ok, status is ${r.status}, check the react-u5auth configuration (wrong provider or token endpoint?)`); + } + return r.json(); + }) + .then(token => { + const { expires_in } = token; + if (expires_in && Number.isFinite(expires_in)) { + const slackSeconds = 10; + // add 'expires_at', with the given slack + token.expires_at = new Date(new Date().getTime() + expires_in * 1000 - (slackSeconds * 1000)); + } + return token; + }) + .catch(err => { + console.error('ERR (fetch)', err); + throw err; + }); +}