Skip to content
28 changes: 22 additions & 6 deletions src/lib/createAuthContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 <Authenticated /> 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 <Authenticated /> component.`)
}
},[token])

return token
}

Expand All @@ -56,14 +71,15 @@ 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
})
.then(setToken)
.then(() => {
removeCodeFromLocation()
removeVerifierFromStorage({ clientId, storage })
removeVerifierFromStorage({ clientId, storage })
})
.catch(e => {
console.error(e)
Expand All @@ -81,7 +97,7 @@ export default ({
}

return (
<Provider value={{token, ensureAuthenticated}}>
<Provider value={{token, setToken, ensureAuthenticated}}>
{children}
</Provider>
)
Expand Down
35 changes: 35 additions & 0 deletions src/lib/helpers/exchangeRefreshForAccessToken.js
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some auth providers don't provide refresh tokens. Before setting a timer to use the refresh token, we must check if there actually is a 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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see, you have slack here, too. I wouldn't change the value of expires_at, but rather calculate the correct delay for the timer with the help of slack.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what you mean here, I was basically following how you implemented the expired_at in the fetchToken.js. Maybe I don't fully understand

}
return token;
})
.catch(err => {
console.error('ERR (fetch)', err);
throw err;
});
}