Apollo-client: Not documented: How can I setContext (using 'apollo-link-context, etc.) AFTER ApolloClient is initiliazed?

Created on 20 Jun 2019  路  18Comments  路  Source: apollographql/apollo-client

After user logs in need to set auth headers (token), but ApolloClient is already initialized at the top level of my app. Is there any example on the best way to do this? Thanks.

Most helpful comment

the token middleware run on every request so the problem is not with middleware.
it's just a simple issue.
don't pass anything as authorization filed on request header if no token exist

this is wrong

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

this is right

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

All 18 comments

you need to use apollo-link-context. here's an example from my project:

  const authLink = setContext((_, {headers, ...context}) => {
    const token = localStorage.getItem('auth:token');
    return {
      headers: {
        ...headers,
        ...(token ? {Authorization: token} : {}),
      },
      ...context,
    };
  });

  const httpLink = createHttpLink({uri: GRAPHQL_URL});

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

I just set the auth:token in the local storage once user is logged in.

also, there's an example in the docs

After user logs in need to set auth headers (token), but ApolloClient is already initialized at the top level of my app. Is there any example on the best way to do this? Thanks.

Please can you tell me did find the decision? Because i have the same situation. Can you sent the code example please

I stumbled across the same issue. I am retrieving token from localStorage. But the context is set BEFORE the user logs in and hence no token is set in context. I had to reload the page for apollo client to reinitialize after the user logs in.

Got the same issue. Need to change the headers whenever the URL changes.
Anyone solved it yet?

I have a similar problem (outlined here). Has anyone determined where you can set these headers early enough before (a) the first query or (b) mutation and (c) submissions are established? Right now I my Vue component is created along with the Apollo Client before my Single Sign On is completed and there is no way that I can find to set headers other than using the getAuth callback but that requires a synchronous response which is incompatible with my Single Sign On (OKTA) library which returns a promise using async/await.

Useful note: setContext from apollo-link-context will run whenever a network request is made. If it's not running at all for a query, it's probably because Apollo is using cached data.

Remember that default fetchPolicy is "cache-first" which means: if data exists in the cache, use it and don't even bother making a network request.

I ended up hard-redirecting the browser after sign in and sign out (window.location.href = ... or something like that). This way the whole app gets reinitialized. Not perfect, but it works for now.

I used middleware so that on every request I'll pass the token, taking it from local storage, please see these docs https://www.apollographql.com/docs/react/networking/network-layer/#middleware

The sample code

...
import { ApolloLink, concat } from "apollo-link";
...

...
const contextLink =  new ApolloLink((operation, forward) => {
  const token = localStorage.getItem("userToken") || null;

  operation.setContext({
    headers: {
    authorization: token
  }
});

...
const client = new ApolloClient({
  assumeImmutableResults: true,
  cache: new InMemoryCache({ fragmentMatcher, freezeResults: true }),
  link: concat(contextLink, httpLink, errorLink)
});

@youbek, thanks for the insight and code snippet. Our Vue webapp, uses Vue-Apollo for the Apollo Client interface and we use Okta's library as our OAuth/OIDC provider to generate an access token. Because the accessToken can expire, and be refreshed at any time, we use the callback to ensure each invocation by the ApolloClient has the latest and a valid Access Token furnished by Okta's property accessors rather than going to localStorage directly; however, because it is asynchronous, it doesn't quite work. What I was seeking in this Reported Issue is for a way to make Vue Apollo's getAuth callback to block and wait on the asynchronous result provided by Otka. Hope this clarifies the problem I am encountering.

Proposed localStorage "workaround" with redirect-after-login is nonsense, because in most browsers if user has disabled cookies, we don't have access to localStorage.

So how do we properly change headers after user logs in? How to do it without localStorage?

the token middleware run on every request so the problem is not with middleware.
it's just a simple issue.
don't pass anything as authorization filed on request header if no token exist

this is wrong

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

this is right

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

Hi @EhsanSarshar, yes, confirming that you're absolutely correct in that the setContext middleware runs on each request as you note.

It depends on a requirement, but if you want to delay the link _itself_, my lib may help: https://github.com/dai-shi/apollo-link-lazy

Getting a forward is not a function error when using any of the solutions here, any insight on what I might be missing ?

Has anyone got this to work when the token is returned from a promise and without using localstorage? (For example Okta, AuthO, Microsoft Authentication Lib (msal.js))

I got this to work with https://github.com/AxaGuilDEv/react-oidc, which is a React context wrapper for oidc-client.

Not sure if it's applicable for everyone's use-case here,but copying this template worked great for me: https://github.com/osstotalsoft/generator-webapp-rocket/blob/master/generators/app/templates/infrastructure/src/apollo/AuthApolloProvider.js

What it does is use ApolloProvider as a wrapper component composed with a context hook. Just think of this hook as an async function that eventually resolves and returns a bearer token. This bearer token is then passed as an argument into the apollo client object and its links.
I guess the other trick is returning early if the result from the promise is still loading (i.e. we haven't gotten our bearer token yet).

Was this page helpful?
0 / 5 - 0 ratings