Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to deal with refresh tokens? #248

Open
loick opened this issue Oct 28, 2024 · 4 comments
Open

How to deal with refresh tokens? #248

loick opened this issue Oct 28, 2024 · 4 comments

Comments

@loick
Copy link

loick commented Oct 28, 2024

Hi, I hope this is not a duplicate, I didn't find anything related to it though.

I had a question regarding this library: how can we deal with refresh tokens with the useWebSocket hook?

I used to implement the WebSocket myself, but I suffered from connection loss (that's my guess), and I decided to give it a try with react-use-websocket.

I'm using an authenticated application, and I have an access token to do so. On the WebSocket context, I'm giving this auth token as a query parameter of the websocket URL:
wss://www.myurl.com?token={token}

It works fine like that, however I wonder how to refresh this token if it becomes invalid. I used to do something like this with vanilla WebSockets:

socket.onclose = async (event) => {
      if (event.code === 4401 && event.reason === 'AuthTokenExpired') {
        await refreshAuthTokens()

        // Retry the connection with the new token
        void createSocket()
      }
    }

On the useWebSocket hook, I can do something close to it:

{
shouldReconnect: () => true,
retryOnError: true,
onClose: async (event) => {
        if (event.code === 4401 && event.reason === 'AuthTokenExpired') {
          await refreshAuthTokens()
        }
      },
}

But I wonder if the retry will be executed properly after that, "automatically". If not, can we have access to a method to retry manually on close or on error?

Thanks in advance for your feedback 🙏

@csvan
Copy link

csvan commented Nov 18, 2024

You can memoize the connection URL based on the token

const accessToken = useToken();
const socketURL = useMemo(() => `${apiPath}/?token=${accessToken}`);
const socket = useReactWebSocket(socketURL);

@robtaussig
Copy link
Owner

robtaussig commented Nov 18, 2024

@loick Can you give an example what refreshAuthTokens does? Is it generating a new auth token value? Or is it calling an api to extend an expiration date? If the former, then I'd do something like this:

const [authToken, setAuthToken] = useAuthToken(); //A custom hook of yours to fetch the authToken
const websocketApi = useWebSocket(`wss://www.myurl.com`, {
  queryParams: {
    token: authToken,
  },
  shouldReconnect: () => true,
  retryOnError: true,
  onClose: async (event) => {
    if (event.code === 4401 && event.reason === 'AuthTokenExpired') {
      const newAuthToken = await refreshAuthTokens();
      setAuthToken(newAuthToken);
    }
  },
}
}, Boolean(authToken));

If it's the latter, then I think your code should work just fine. Any components that are connecting with the same token will keeping retrying until successful, which should happen once the authToken is refreshed by refreshAuthTokens.

@marcortw
Copy link

marcortw commented Dec 8, 2024

I faced the same problem like @loick and used a similar approach as described by @robtaussig, but I kind of had to additionally introduce a recurring setTimeout to fetch new tokens (i'm using fetchAuthSession with amplify). As a side note, I also followed the pattern described in #217 (comment) to cater with long-term disconnects of users.

However, I did not use queryParams explicitly but instead just constructed my own url containing my token. Now I wonder how this works if i have 'share: true' set? It seems like in some edge case scenarios I might end up with two open websockets because I might have two valid tokens at the same time - each constructing a unique url, valid at the time of socket session setup.

Is queryParams considered part of the "uniqueness" of a websocket url when share = true?

@marcortw
Copy link

Found the answer (=yes) to my question on https://github.com/robtaussig/react-use-websocket?tab=readme-ov-file#async-urls:

It's important to note, however, that other rules still apply -- namely, that if the function reference changes, then it will be called again, potentially instantiating a new WebSocket if the returned url changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants