2

I am trying to implement the Authentication workflow officially described in the Apollo docs as I did before in other projects, but this time using Gatsby.

The idea seems pretty straightforward. Need to create/update gatsby-browser.js like when using redux but to initialise the ApolloClient and pass through ApolloProvider.

Something like:

import React from 'react'
import { Router } from 'react-router-dom'
import { ApolloProvider } from 'react-apollo'
import { ApolloClient } from 'apollo-client'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { setContext } from 'apollo-link-context'

const httpLink = createHttpLink({
  uri: process.env.ENDPOINT_URI,
  credentials: 'same-origin',     
})

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

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache().restore({}),
})

exports.replaceRouterComponent = ({ history }) => {
  const ConnectedRouterWrapper = ({ children }) => (
    <ApolloProvider client={client}>
      <Router history={history}>{children}</Router>
    </ApolloProvider>
  )

  return ConnectedRouterWrapper
}

Then login using

<Mutation
  mutation={LOGIN_MUTATION}
  refetchQueries={[{ query: CURRENT_USER_QUERY }]}
>
  {/*...*/}
</Mutation>

and in another Component with something like the Status (where, if logged in shows x otherwise y):

<Query query={CURRENT_USER_QUERY} fetchPolicy="network-only">
{/*...*/}
</Query>

The problem

With this configuration, when the login receives data (so, credentials are ok), I save the token received in localStorage but the Status component do the Query with the previous (empty) token, so shows logged out, also doing something like history.push('/') after login.

Refreshing the page, since the item in localStorage was saved before, the Status shows as logged in.

Expected behaviour

The idea is to have the Status component updated after login avoiding doing a refresh of the page, for this reason I added refetchQueries={[{ query: CURRENT_USER_QUERY }]}.

I tried some things like pass props (no needed in case I change the route and use withRouter in the Status component), but seems to late, the Query was fired without the new token.

Lionel T
  • 1,559
  • 1
  • 13
  • 30

2 Answers2

0

The main issue here is probably your Status component needs to be re-render only after the the token saved into localStorage. Right now it's probably re-render too early.

You can try wrapping your Status component with another wrapper component, and check for the token existence before rendering the Status component.

Andy
  • 5,287
  • 2
  • 41
  • 45
  • Thanks Andy for the response, I tried what you said but the problem persists. So, after login, console.logging the localStorage item in the _Status_ Component seems to be set, so I conditionally rendering the Component, but seems that the request sent to server doesn't have the token. – Lionel T Jun 01 '18 at 08:23
0

I believe your issue is related to how the links are defined. IMO, the AuthLink should be defined before the HttpLink (as middleware):

const client = new ApolloClient({
  link: ApolloLink.from([
    authLink,
    httpLink
  ]),
  cache: new InMemoryCache().restore({}),
})

While the documentation says that concat and from do the same thing, order is important. You want to have the context set before sending the request.

elmasse
  • 221
  • 2
  • 4
  • seems that the issue is related to _Apollo_. Here are three open issues that expand the situation: [2009](https://github.com/apollographql/react-apollo/issues/2009), [630](https://github.com/apollographql/apollo-link/issues/630) and [461](https://github.com/apollographql/apollo-link/issues/461). – Lionel T Jun 01 '18 at 14:47