apollographql/apollo-link

setContext in the errorLink is not updating the global header token

Opened this issue · 0 comments

Hi,

I am having an issue with updating the token in the headers after I call a cognito refreshToken operation.
Can anyone help me what am I going wrong because this should work like this for sure.
Thank you for everyone's help in advance.

Expected Behavior
Expected behavior would be that after the refresh token call is executed (1 hour after the initial login) we get the new token from cognito (this part works) and we updated the global header so all future calls have the new token in their header.

Actual Behavior
Actual behavior is that first the token is registered globally but then 1 hour later when it expires and I call refreshtoken the global header doesn't get updated with the new setContext call but instead every graphql call from this point on first returns a 401 error then it goes into the refreshtoken algorithm and executes the call with the newly calculated token but this doesn't get stored globally (although this token again would be valid for 1 hour) instead next call again returns the 401 error and so on.
Although the operation which failed get recalled with the newly calculated token due to the forward operation but this way after the first hour passes every call get's called double due to the first 401 instead of a global token update.

A simple reproduction

 const token = localStorage.getItem("token");
  const cache = new InMemoryCache();

  const httpLink = new HttpLink({
    uri: `${config.endpoint}/graphql`,
  });

  const authLink = setContext((_, { headers }) => {
    return {
      headers: {
        ...headers,
        authorization: token ? `Bearer ${token}` : '',
      },
    };
  });

  const errorLink = onError(({ graphQLErrors, networkError, operation, forward }) => {
    console.log("graphQLErrors, networkError", graphQLErrors, networkError, networkError?.statusCode);
   
    // User access token has expired
    if (networkError && networkError.statusCode === 401) {
      // Let's refresh token 
      return new Observable(async observer => {
        const cognitoUser = await Auth.currentAuthenticatedUser();
        const { refreshToken } = await Auth.currentSession();
        if (refreshToken && token) {
          cognitoUser.refreshSession(refreshToken, (e, session) => {
            if (e) {
              console.error('Unable to refresh session', e);

              observer.error(e);
              return
            }
            console.log(`%c 👌 REFRESH COGNITO TOKEN, error ${e}`, "color:blue");
            console.log("session", session); // the new token here is valid for an other 1 hour so I would like to update it globally

            const token = session.idToken.jwtToken;
            localStorage.setItem("token", token);
            operation.setContext(context => ({
              ...context,
              headers: {
                ...context.headers,
                authorization: token ? `Bearer ${token}` : '',
              }
            }));

            const subscriber = {
              next: observer.next.bind(observer),
              error: observer.error.bind(observer),
              complete: observer.complete.bind(observer)
            };
            forward(operation).subscribe(subscriber);

          })
        }
      });

    }

  });

  const link = ApolloLink.from([
    authLink,
    errorLink,
    httpLink
  ]);

  return new ApolloClient({
    link,
    cache,
  });