Search code examples
react-nativereduxgraphqlapollographcool

Apollo Client Delaying the Authorization Header


I am using Apollo (with Graph Cool), redux, and Auth0 in a React-Native app. I am trying to delay the queries and mutations until the header is set.

The idToken is stored in Async Storage, and is therefore a promise. I can't use redux to pass the token, because that would create a circular dependency.

When the user logins in for the first time or the token has expired, the queries are sent before header is set, which means I get the error Error: GraphQL error: Insufficient Permissionsenter image description here

How can I delay the queries until the token is found and added to the header? I have been searching three main solutions:

  1. Add forceFetch: true; This seems to be part of an earlier implementation of the Apollo client. Even if I find the equivalent, the app still fails on the first attempt to fetch.
  2. Reset the store (rehydrate?) upon logging in. This is still asynchronous so I don't see how this could affect the outcome.
  3. Remove all mutations and queries from login itself, but due to the progress of the app, this is not feasible.

Some snippets:

const token = AsyncStorage.getItem('token');
const networkInterface = createNetworkInterface({ uri:XXXX})

//adds the token in the header
networkInterface.use([{
    applyMiddleware(req, next) {
        if(!req.options.headers) {
            req.options.headers = {}
        }
        if(token) {
            token
                .then(myToken => {
                    req.options.headers.authorization = `Bearer ${myToken}`;
                })
                .catch(err => console.log(err));   
        }
        next(); // middleware so needs to allow the endpoint functions to run;
    },
}]);

// create the apollo client;
const client = new ApolloClient({
    networkInterface,
    dataIdFromObject: o => o.id
});

and

const store = createStore(
  combineReducers({
    token: tokenReducer,
    profile: profileReducer,
    path: pathReducer,
    apollo: client.reducer(),
  }),
  {}, // initial state
  compose(
      applyMiddleware(thunk, client.middleware(), logger),
  )
);

Solution

  • I'm not certain this will work without a reproduction app, mostly because I don't have an app of your structure set up, but you're hitting this race condition because you are calling next() outside of your async chain.

    Calling next() where it is currently will tell the client to continue on with the request, even if your token isn't set. Instead, let's wait until the token comes back and the header gets set before continuing on.

    networkInterface.use([{
      applyMiddleware(req, next) {
        if(!req.options.headers) {
          req.options.headers = {}
        }
        AsyncStorage.getItem('token')
          .then(myToken => {
             req.options.headers.authorization = `Bearer ${myToken}`;
          })
          .then(next)  // call next() after authorization header is set.
          .catch(err => console.log(err));   
      }
    }]);