question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Cannot get the new AccessToken in AuthLink after updating it in handleFetch

See original GitHub issue

Every example using this package shows that setAccessToken can be called in handleFetch and the new Access Token can be used in the next link.

But my AuthLink is retrieving the old accessToken

In the next link, I am setting the new state in handleFetch

Refresh Link

export const useRefreshLink = () => {
  const { getAccessToken, setAccessToken, refreshToken } = useAuth()

  return new TokenRefreshLink<AuthPayload>({
    accessTokenField: 'payload',
    isTokenValidOrUndefined: () => {
      const accessToken = getAccessToken()
      console.log('Old token', accessToken)
      if (!accessToken) return true
      return isTokenValid(accessToken)
    },
    fetchAccessToken: async () => {
      const refreshResponse = await refreshToken()
      const fakeResponse = new Response()
      return { ...fakeResponse, ...refreshResponse }
    },
    handleResponse: () => (res: Response & AuthPayload) => {
      const payload = {
        accessToken: res.accessToken,
        profile: res.profile,
      }
      return { payload }
    },
    handleFetch: (payload) => {
      const { accessToken, profile } = payload
      console.log(`New token @ ${new Date()}`, accessToken)
      setAccessToken(accessToken)
    },
    handleError: (err) => {
      console.log('err', err)
    },
  })
}

In htis link however, I am getting the old token

Auth Link

export const useAuthLink = () => {
  const { getAccessToken } = useAuth()

  return new ApolloLink((operation, forward) => {
    const accessToken = getAccessToken()

    if (accessToken) {
      console.log(`Auth Token @ ${new Date()}`, accessToken)
      operation.setContext(({ headers = {} }) => ({
        headers: {
          ...headers,
          authorization: `bearer ${accessToken}`,
        },
      }))
    }

    return forward(operation)
  })
}

This useEffect shows that the token is updated AFTER I retrieve the accessToken in my AuthLink

useAuth

export const useAuthLogic = (): AuthContextType => {
  const [accessToken, setAccessToken] = useState<string | null>(null)

  useEffect(() => {
    console.log('AccessToken changed @ ', new Date())
  }, [accessToken])

  const getAccessToken = () => accessToken
  // ...
}

Here is my dev console showing that the token is updated AFTER I try to retrieve the new token in my AuthLink

Old token eyJhbGciOiJI[...]hW1LC4
Token expired: true
New token eyJhbGciOiJI[...]j4yb4
Auth Token: eyJhbGciOiJI[...]hW1LC4
AccessToken changed

Error: not authenticated
    at new ApolloError2 (index.ts:71:5)
    at QueryManager.ts:246:15
    at both (asyncMap.ts:30:30)
    at asyncMap.ts:19:47
    at new Promise (<anonymous>)
    at Object.then (asyncMap.ts:19:16)
    at Object.next (asyncMap.ts:31:39)
    at notifySubscription (module.js:132:18)
    at onNotify (module.js:176:3)
    at SubscriptionObserver2.next (module.js:225:5)

I am using React 18 and this is my links order

const appClient = getApolloClient('app', [refreshLink, authLink, httpLink])

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
eaklcommented, Jun 3, 2022

that will not help. I think you could play w expiration date by validating it w/ one minute gap, so you will start refreshing before your token will actually be expired. Ofc, this is not perfect decision, but right now I don’t see any 100%-save solutions

Use case is as follow:

  • Login. Access Token is sent to the client and Refresh Token is sent in the cookie
  • Refresh the page. Refresh Token mutation is fired, new Access Token is sent to the client and new Refresh Token is sent in the cookie.
  • Now, let’s say the browser is idle for an hour. Token is expired since 45min (Access Token life was 15min)
  • I click on a button with an event handler that fires a mutation.

The expected behavior is

  • Refresh Link should see that the Token is expired, refetch a new access token and put it in the state.
  • Auth Link (next one in the chain) get the new access token from the state and put it in the header
  • httpLink make the request with the new access token in the header and the mutation executes correctly.

Instead, the issue is that between RefreshLink (that set the state) and AuthLink (that gets the state), the new state did not have time to update, hence in the AuthLink, I get the old state (old access token). I think this is due to the fact that useState is asynchronous.

I used this library react-useStateRef to get the most recent state.

Now:

  • In the refreshLink, inside the handleFetch function I can setAccessToken()
  • In the next Link in the chain I can call AccessTokenRef.current to get the most updated state and have the correct access Token in the header.
0reactions
newsiberiancommented, Jun 3, 2022

that will not help. I think you could play w expiration date by validating it w/ one minute gap, so you will start refreshing before your token will actually be expired. Ofc, this is not perfect decision, but right now I don’t see any 100%-save solutions

Read more comments on GitHub >

github_iconTop Results From Across the Web

Issues · newsiberian/apollo-link-token-refresh - GitHub
Cannot get the new AccessToken in AuthLink after updating it in handleFetch. #53 opened on Jun 3 by eakl · 7. Check JWT...
Read more >
Refreshing access and refresh tokens via Apollo in React - Help
Even if I receive new tokens, I understand that I have no idea how to send the ... how to refresh access and...
Read more >
Apollo GraphQL : Async Access Token Refresh - Able
In a real world scenario : You'll be using a refresh token to get and store a new pair of access and refresh...
Read more >
Message "Support for password authentication was removed ...
From your GitHub account, go to Settings → Developer Settings → Personal Access Token → Generate New Token (Give your password) → Fillup ......
Read more >
Working with Access and Refresh Tokens Using Next.js and ...
I recently started learning Next.js to build a full-stack React app ... how to send the access token (authLink) and when to request...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found