auth0 / auth0-react

Auth0 SDK for React Single Page Applications (SPA)
MIT License
866 stars 252 forks source link

Calling getAccessTokenSilently after async code doesn't update token details #762

Closed jonorogers closed 3 months ago

jonorogers commented 3 months ago

Checklist

Description

When saving a user profile in my web app, my code looks like this:

  1. JS event handler calls backend API with updated form data
  2. Backend API uses C# management SDK to update Auth0 user profile
  3. On successful API call, JS event handler calls getAccessTokenSilently({ cacheMode: 'off' }) to update user token details

What I expect to happen is that getAccessTokenSilently({ cacheMode: 'off' }) calls the Auth0 API and updates the user token details. However, in my app, this does not happen. I am calling an RTK Query mutation before the call to the Auth0 SDK, and this appears to be the issue:

This is my app code which is not working:

const [updateUser] = useUpdateUserMutation();

const onSave = async () => {
    const updatedUser = await updateUser(request).unwrap()
    await getAccessTokenSilently({ cacheMode: "off" });
};

However, this does work (moving getAccessTokenSilently to the event queue?):

const [updateUser] = useUpdateUserMutation();

const onSave = async () => {
    const updatedUser = await updateUser(request).unwrap()
    setTimeout(async () => await getAccessTokenSilently({ cacheMode: "off" }), 0);
};

This code also works (replacing the updateUser call with a random await to an external api):

const onSave = async () => {  
    const fact = await fetch("https://catfact.ninja/fact");  
    console.log(fact);  
    await getAccessTokenSilently({ cacheMode: "off" });  
};

I'm not enough of an expert on the internals of JS to fully understand the differences between the examples; which is why I'm creating this issue. For now, I'm using the setTimeout workaround to get things to work, but I want to know why there is a differencve, and why the call after the mutation await fails.

Note that when I say fails - when I run getAccessTokenSilently({ cacheMode: 'off', detailedResponse: true }) I can see token details returned, but there is no network request issued. At one point I had not correctly configured the app for refresh tokens and I there was not even an error generated when it was failing - but if called 'correctly' it would immediately throw a JS error.

I've created a community topic with some more details here

Reproduction

I haven't yet tried to create a mashup of Redux Toolkit's starter app (using vite) and your starter app using CRA - apologies but I was hoping the info above would give enough info to dig into it without that time investment.

Additional context

No response

auth0-react version

2.2.4

React version

18.2.0

Which browsers have you tested in?

Chrome, Firefox

nandan-bhat commented 3 months ago

Hi @jonorogers,

I can't replicate the issue with the provided preconditions. Based on your description, it doesn't seem to be an SDK-related bug.

However, I'm willing to analyze it further if you give more details about const updatedUser = await updateUser(request).unwrap(). Since it's a promise, it needs to be resolved before await getAccessTokenSilently({ cacheMode: "off" }) is executed.

Have you confirmed that updateUser(request).unwrap() is getting resolved? Problem seems to be in const updatedUser = await updateUser(request).unwrap()

const onSave = async () => {
    const updatedUser = await updateUser(request).unwrap();
    console.log(updatedUser); // Did this line print?
    await getAccessTokenSilently({ cacheMode: "off" });
};

This issue seems more related to your implementation than to our SDK, but I can investigate further if you provide more details.

Also, if possible, could you attempt to replicate this bug in our Auth0 React Samples? This would help us identify the exact use-case.

jonorogers commented 3 months ago

Hi @nandan-bhat ,

Thanks for looking into it, yeah it's definitely a hard one to recreate. Yes; the promise is being resolved; I've tried a bunch of different ways to ensure it's resolved; see below as one example:

await getAccessTokenSilently({ cacheMode: "off" });
  await updateUser(request)
      .unwrap()
      .then((x) => {
          getAccessTokenSilently({ cacheMode: "off" }).then(
              (value) => console.log(value)
          );
      });

Note that in the above example, I can see the HTTP request going out for the first call to getAccessTokenSilently; but not the second.

As to your comment about if it printed - yes it always does print a return value; if I debug the id_token on jwt.io I can see stale values when there's no HTTP request, correct values if the request is sent..

I've tried recreating it in your sample react application and I can't - even after a similar mutation through RTK Query to a remote API, the call to getAccessTokenSilently still works. I can't figure it - but I have a workaround; so I'll just get on with my life not knowing why it's doing what it's doing. Weird one.

nandan-bhat commented 3 months ago

Hey @jonorogers,

Thanks for the clarification.

Could you please confirm if the call to fetch("https://catfact.ninja/fact") is visible in your browser's "Network" tab when executing the following code:

const onSave = async () => {
    const updatedUser = await updateUser(request).unwrap();
    console.log(updatedUser); 
    const fact = await fetch("https://catfact.ninja/fact");
}

I know it might sound trivial, but I just want to double-check.

jonorogers commented 3 months ago

Could you please confirm if the call to fetch("https://catfact.ninja/fact") is visible in your browser's "Network" tab when executing the following code:

Hi @nandan-bhat yup, that line (executing after the RTK query mutation) executes an HTTP request and returns a valid cat fact

nandan-bhat commented 3 months ago

Hi @jonorogers ,

Since we couldn't reproduce the problem, we're closing this issue. If you have any other questions or concerns regarding this matter, please feel free to reopen it.