oidc-client icon indicating copy to clipboard operation
oidc-client copied to clipboard

useOidcAccessToken behavior is patchy, gives expired token after refresh token routine

Open rbalyan opened this issue 2 years ago • 17 comments

Issue and Steps to Reproduce

We cannot deterministically rely on the hook - useOidcAccessToken. For the first time we get a correct token but after token refresh we get the previous token that has expired. If we fetch the accessToken from localStorage where idc lib stores it, we get a good token. Most of the issues seen in this area are around after token refresh.

Steps:

  1. Login and check accessToken(will be good one)
  2. Wait for refresh token to trigger and useOidcAccessToken() to trigger the rerender
  3. mostly you will find token is older one.

Versions

exists in latest (v6.10.18) as well

Screenshots

Expected

hooks should deterministically provide latest and greatest accessToken

Actual

After first refresh most of the time the token received is previous one (expired)

Additional Details

  • Installed packages:

rbalyan avatar Nov 16 '22 17:11 rbalyan

Thank you @rbalyan for the issue. I look a it today!

guillaume-chervet avatar Nov 17 '22 05:11 guillaume-chervet

Hi @rbalyan , i did not sucess to reproduce the problem. Dobyou have a sample of your configuration ?

guillaume-chervet avatar Nov 17 '22 19:11 guillaume-chervet

Hi @guillaume-chervet I have the same issue The config is (without serviceWorker)

client_id: 
    redirect_uri:
    scope: 
    authority: 
    refresh_time_before_tokens_expiration_in_second: 10,
    service_worker_only: false, 
    storage: localStorage,
    authority_configuration: {
   
    },

Zhuohui-Li avatar Nov 28 '22 19:11 Zhuohui-Li

The example code

export const PingIDContext = React.createContext();

export const PingIdProvider = ({ children, envData }) => {
	const { login, logout, isAuthenticated } = useOidc("configName");
	const { oidcUser, oidcUserLoadingState } = useOidcUser("configName");
	const oidcAccessTokenState = useOidcAccessToken("configName");

	const decorateToken = useCallback(() => {
		const { accessToken, accessTokenPayload } = oidcAccessTokenState; // the accessToken here is always the previous one
       // use the accessToken to do other stuffs
	}, [oidcAccessTokenState]);

	// sync token on load
	useEffect(() => {
		if (!isLoaded) {
			decorateToken();
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);
};

PingIdProvider.propTypes = {
	children: PropTypes.element.isRequired,
	envData: PropTypes.shape({
		//...
	}).isRequired,
};

Thanks a lot

Zhuohui-Li avatar Nov 28 '22 20:11 Zhuohui-Li

my impression is that somehow the state of the hook changes before the actual token is updated in the token state. some timing issue. Zhuohui has provided a reasonable code snippet here, only thing I would add is that in the decorateToken function if you try to validate expiry of token then you will see it is expired most of the times and will exactly match ti the token that just expired

rbalyan avatar Nov 28 '22 21:11 rbalyan

Hi @guillaume-chervet This issue happens if I try to read id_token like const { idTokenPayload } = useOidcIdToken(configName); If I remove this line, I can get the right access token

Zhuohui-Li avatar Nov 29 '22 17:11 Zhuohui-Li

Thank you @Zhuohui-Li @rbalyan your feedback help are very helpful !

guillaume-chervet avatar Nov 29 '22 20:11 guillaume-chervet

hi @rbalyan, @Zhuohui-Li, I do not success to reproduce it => https://github.com/AxaGuilDEv/react-oidc/pull/913/files Which OIDC server are your using?

guillaume-chervet avatar Nov 29 '22 20:11 guillaume-chervet

here the published demo by the pullrequest => https://black-rock-0dc6b0d03-913.westeurope.1.azurestaticapps.net/

guillaume-chervet avatar Nov 29 '22 20:11 guillaume-chervet

Hi @guillaume-chervet Thanks a lot. I didn't get you but we are using PingId as our auth provider

Zhuohui-Li avatar Nov 30 '22 15:11 Zhuohui-Li

Hi @guillaume-chervet The token comes from token.oauth2 The response from auth provider is

{
access_token: ""
expires_in: 899
refresh_token: ""
token_type: "Bearer"
}

Is it because that the id token is not in the response?

Zhuohui-Li avatar Dec 04 '22 05:12 Zhuohui-Li

Thank you @Zhuohui-Li , I think it is the information I needed :)

guillaume-chervet avatar Dec 04 '22 10:12 guillaume-chervet

I @Zhuohui-Li , I removed openid scope from my demo so I have only the access_token now in the demo.

https://github.com/AxaGuilDEv/react-oidc/pull/913/files but I still cannot reproduce your bug:

https://black-rock-0dc6b0d03-913.westeurope.1.azurestaticapps.net/

DO you have any idea that can help ?

guillaume-chervet avatar Dec 06 '22 09:12 guillaume-chervet

Hi @guillaume-chervet The only difference I can find is I wrapper the hook in the react context. Also, could you check whether the access token from const { accessToken } = oidcAccessTokenState;in line 13 is same as the access token in the local storage? Ideally they should have same exp Thanks a lot

Zhuohui-Li avatar Dec 06 '22 15:12 Zhuohui-Li

react version could be at play here too, just a thought

rbalyan avatar Dec 06 '22 16:12 rbalyan

Which react version are you using @rbalyan ?

guillaume-chervet avatar Dec 12 '22 19:12 guillaume-chervet

Hi @guillaume-chervet [email protected] if it helps

Zhuohui-Li avatar Dec 14 '22 14:12 Zhuohui-Li