Hi,
Having recently struggled with the token refresh, in react, I’ve scoured this site, and come up with the following recommendations, which work well.
-
don’t use the refreshToken, unless you want to keep a login alive across multiple days. In our use case, we delegate the life of an Okta accessToken to be the life of the okta session.
-
make sure to move the token to session storage, and force the refresh to happen before it expires
const config = {
issuer: ISSUER,
clientId: clientId,
redirectUri: redirectUri,
scope: SCOPES.split(/\s+/),
pkce: false,
responseType: ['token', 'id_token'],
tokenManager: {
storage: 'sessionStorage',
expireEarlySeconds: 1800,
}
}
here my accessToken is set to live for 1 hour. At 30 minutes we go get a new one. However this does not always fire, often due to inactivity, or the browser JS engine optomization, so we added:
a timer to check the auth every couple minutes. This does nothing (network wise) when inside the 30 minutes.
useEffect(() => {
//https://github.com/okta/okta-oidc-js/issues/744#
const interval = setInterval(() => {
if (isAuthComplete) {
authService.updateAuthState();
}
}, 1000 * 60 * 2);
return () => {
clearInterval(interval);
}
}, );
please note that ant call to authState.IsAuthenticated MUST be protected, as we have witnessed parallel re-auth which causes the okta state to go out of sync. The accessToken is a context stored copy of what is in authState.accessToken, to detect when a new token is received, and store it.
for tracking we decode the tokens:
function parseJwt (token) {
if (token === null || typeof token === “undefined” || typeof token.length === “undefined”) return “”;
var base64Url = token?.split(’.’)[1];
var base64 = base64Url?.replace(/-/g, ‘+’)?.replace(/_/g, ‘/’);
var jsonPayload = decodeURIComponent(atob(base64)?.split(’’)?.map(function© {
return ‘%’ + (‘00’ + c?.charCodeAt(0)?.toString(16))?.slice(-2);
})?.join(’’));
return JSON.parse(jsonPayload);
};
var parsedAccessToken = parseJwt(accessToken)
var parsedIdToken = parseJwt(authState.idToken)
console.log(new Date().toLocaleTimeString()+":interceptor Ac Token iat: “+new Date(parsedAccessToken?.iat1000)?.toLocaleTimeString() + " exp: "+new Date(parsedAccessToken?.exp1000)?.toLocaleTimeString()
+ new Date().toLocaleTimeString()+”:interceptor ID Token iat: "+new Date(parsedIdToken?.iat1000 )?.toLocaleTimeString() + " exp: "+new Date(parsedIdToken?.exp1000 )?.toLocaleTimeString());
and log the received, and expire times, as well as detect if the access authState.accessToken has changed. Again I never access the okta objects in my application, only copies.
we store isAuthComplete as a context variable to protect calls to authState.isAuthenticated
when we detect a changed accessToekn we update the copy of authState in our context.
if (isAuthComplete
&& !isGetUserInProgress.current //Wait for getUser to complete
&& authState.accessToken !== accessToken) {
var parsedAccessToken = parseJwt(accessToken)
var parsedIdToken = parseJwt(authState.idToken)
console.log(new Date().toLocaleTimeString()+":New Ac Token iat: "+new Date(parsedAccessToken?.iat*1000)?.toLocaleTimeString() + " exp: "+new Date(parsedAccessToken?.exp*1000)?.toLocaleTimeString()
+" :New ID Token iat: "+new Date(parsedIdToken?.iat*1000 )?.toLocaleTimeString() + " exp: "+new Date(parsedIdToken?.exp*1000 )?.toLocaleTimeString());
console.log(new Date().toLocaleTimeString()+":New Ac Token: " + authState.accessToken
+" :New ID Token: " + authState.idToken);
if ((typeof authState.accessToken === "undefined" || typeof authState.idToken ==="undefined")
&& !isLogoutInProgress.current) {
isLogoutInProgress.current=true;
//The user got logged out
alert(" You have been signed out. Click OK to Complete Log out... " );
HeaderSignOutHandler();
} else {
SetAuthState(authState);
}
}
We detect when the authState becomes undefined, AKA okta session has expired.
we use axios interceptors to both set the accessToken (from a copy in context) and detect 401 errors, as even the above method is not fool-proof.
This is not the fault of the Okta libraries, but of the browser JS optimizations.
I’ve been able to sucessfully continually renew the accessToken for the life of the okta sign on session (tested by being logged into our app, logging out of okta, then waiting for the app to go get a new token.)
This method also has the advantage of accelerating the develop cycle, by changing
expireEarlySeconds: 1800, to just shy of an hour.
Hope this helps.
Ivan