Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
vonwao opened this issue Jun 20, 2019 · 19 comments

Comments

@vonwao
Copy link

vonwao commented Jun 20, 2019

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.

@redmundas
Copy link

redmundas commented Jun 21, 2019

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.

@redmundas
Copy link

also, there's an example in the docs

@jocoders
Copy link

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

@ishanshukla97
Copy link

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.

@ivdma
Copy link

ivdma commented Oct 28, 2019

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

@CVANCGCG
Copy link

CVANCGCG commented Dec 6, 2019

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.

@gustavopch
Copy link

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.

@ivdma
Copy link

ivdma commented Dec 6, 2019

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.

@youbek
Copy link

youbek commented Jan 26, 2020

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

@youbek
Copy link

youbek commented Jan 26, 2020

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)
});

@CVANCGCG
Copy link

@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.

@Traxo7
Copy link

Traxo7 commented Jan 29, 2020

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?

@byteab
Copy link

byteab commented Feb 7, 2020

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,
    };
  });

@CVANCGCG
Copy link

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

@dai-shi
Copy link

dai-shi commented Apr 14, 2020

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

@Morphexe
Copy link

Morphexe commented Sep 3, 2020

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

@TellonUK
Copy link

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))

@jkhaui
Copy link

jkhaui commented Dec 27, 2020

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).

@hwillson
Copy link
Member

It doesn't sound like there is an outstanding issue here, so closing. Thanks!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests