Skip to content

Token refresh query may use stale refreshToken due to closure in AuthUserProvider #16040

@Anant3008

Description

@Anant3008

Describe the bug

The token refresh mechanism in the frontend may use a stale refresh token due to how the React Query queryFn captures variables from the render scope in AuthUserProvider.

Because the refresh query uses a static queryKey and the refresh token is captured in a closure, the query function may continue using an old refresh token even after the token has been rotated and updated in localStorage. This leads to unexpected session expiration or authentication failures during long-running sessions.

Relevant File

src/Providers/AuthUserProvider.tsx

Current Implementation (Simplified)

useQuery({
  queryKey: ["user-refresh-token"],
  queryFn: () => authApi.refresh({ refresh: refreshToken }),
  refetchInterval: careConfig.auth.tokenRefreshInterval,
});

The Issue:
Here, refreshToken is read from the render scope. If the backend rotates the refresh token and a new token is written to localStorage, the queryFn may continue using the old token until the component re-renders. Because the queryKey is static (["user-refresh-token"]), React Query does not necessarily recreate the queryFn, allowing the stale token to persist across refresh intervals.


To Reproduce

  1. Log in to the application normally.
  2. The frontend stores care_access_token and care_refresh_token in localStorage.
  3. The token refresh query runs periodically using refetchInterval.
  4. The backend rotates the refresh token and returns a new one.
  5. The frontend updates the refresh token in localStorage.
  6. The next refresh interval fires.
  7. The queryFn still uses the old refresh token captured in the closure.
  8. The backend rejects the refresh request, causing the session to expire unexpectedly.

Expected behavior

  • The refresh mechanism should always use the latest refresh token stored in localStorage.
  • Token refresh requests should succeed as long as a valid refresh token exists, preventing unexpected session expiration during normal usage.

Screenshots

Not applicable — this is a logic issue in the authentication flow rather than a UI issue.


Environment

  • Desktop: Any (Chrome / Firefox / Safari)
  • Smartphone: Any

Additional context

A safer implementation would ensure the refresh token is read dynamically rather than relying on a value captured during render.

Proposed Fix 1: Dynamic Lookup

queryFn: () => {
  const refreshToken = localStorage.getItem("care_refresh_token");
  return authApi.refresh({ refresh: refreshToken });
}

Proposed Fix 2: Reactive Query Key

Include the refresh token in the query key to ensure React Query recreates the query when the token changes:

queryKey: ["user-refresh-token", refreshToken]

I would be happy to work on implementing a fix for this issue if maintainers confirm the preferred approach.


🚨 DO NOT EDIT BELOW THIS LINE 🚨

Instructions for Requesting Assignment:

To request assignment, please clearly outline your solution and timeline by commenting on the issue using the format below:

Describe your solution clearly:
Provide a detailed explanation of your proposed solution, including your approach, key implementation steps, and relevant examples or references. Mention any dependencies, assumptions, or risks you foresee that might affect your timeline or implementation.

Expected Timeline:

  • End date: [Expected submission date of a completed Pull Request]

Additional Context:
Include any other relevant context, links, screenshots, or resources that support your proposed solution.

🚨 Your assignment may be unassigned if there is no activity or progress within the stated timeline unless communicated clearly and agreed upon.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions