how to add auth0 access token to react-apollo
Asked Answered
S

2

7

I'm trying to add authorization token to the apollo client in react js to let the users login ...

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { ThemeProvider } from 'react-jss';
import Theme from 'resources/theme';
import Routes from 'routes';
import './index.css';
import * as serviceWorker from './serviceWorker';
import { Auth0Provider } from "@auth0/auth0-react";
import 'bootstrap/dist/css/bootstrap.min.css';
import ReactNotification from 'react-notifications-component'
import './components/orders/fonts/NotoSansJP-Regular.otf'
import 'react-notifications-component/dist/theme.css'
import { ApolloProvider , ApolloClient, InMemoryCache } from '@apollo/client';
import { useAuth0 } from "@auth0/auth0-react";


const client = new ApolloClient({
uri: process.env.REACT_APP_API_BACK ,
headers: {
  Authorization: `Bearer ${accessToken}` // doesn’t work 
 },
  cache: new InMemoryCache()
});


ReactDOM.render(
     <Auth0Provider
    domain= {process.env.REACT_APP_AUTH0_DOMAIN }
  clientId= {process.env.REACT_APP_AUTH0_CLIENT_ID}
   redirectUri={window.location.origin}
   audience={process.env.REACT_APP_AUTH0_AUDIENCE}
   scope="warehouse"
   
  >
    <ApolloProvider client={client}> 
    <ThemeProvider theme={Theme}>
        <Router>
        <ReactNotification />
            <Routes />
        </Router>
    </ThemeProvider>,
    </ApolloProvider>
      </Auth0Provider>,
    document.getElementById('root')
);

serviceWorker.unregister();

to get the token i need to import :

import { useAuth0 } from "@auth0/auth0-react";

the add this lines :

const {  getAccessTokenSilently } = useAuth0();

but this cannot be done in index.js i think

to get the token :

 const accessToken = await getAccessTokenSilently

this what i found in the docs and from google search , but i think it cannot be done in my case , most tutorials show how to get the user data ( including the token) in a profile page but that's not wha I want .

i want to pass it to the client in index.js

Sauerkraut answered 25/1, 2021 at 12:15 Comment(0)
H
10

This is what I ended up doing:

import {
  ApolloProvider,
  ApolloClient,
  InMemoryCache,
  HttpLink,
} from '@apollo/client';
import { setContext } from '@apollo/link-context';
import { useAuth0 } from '@auth0/auth0-react';

const ApolloProviderWithAuth0 = ({ children }) => {
  const { getAccessTokenSilently } = useAuth0();

  const httpLink = new HttpLink({
    uri: process.env.REACT_APP_GRAPHQL_URI,
  });

  const authLink = setContext(async (_, { headers, ...rest }) => {
    let token;
    try {
      token = await getAccessTokenSilently();
    } catch (error) {
      console.log(error);
    }

    if (!token) return { headers, ...rest };

    return {
      ...rest,
      headers: {
        ...headers,
        authorization: `Bearer ${token}`,
      },
    };
  });

  const client = React.useRef();

  if (!client.current) {
    client.current = new ApolloClient({
      link: authLink.concat(httpLink),
      cache: new InMemoryCache(),
    });
  }

  return (
    <ApolloProvider client={client.current}>
      {children}
    </ApolloProvider>
  );
};

export { ApolloProviderWithAuth0 };

And then use it as Provider in your App component.

Hernandez answered 16/3, 2021 at 20:12 Comment(0)
C
3

You need to create dedicated component for

function useToken() {
  const {  getAccessTokenSilently } = useAuth0();
  const [token, setToken] = useState(null);

  useEffect(() => { getAccessTokenSilently().then(setToken) }, [])

  return token;
}

function MyApolloProvider({ children }) {
  const token = useToken();

  // useRef instead of useMemo to make sure client is not re-created randomly
  const clientRef = useRef(null);

  if (token && !clientRef.current) {
    // This code should only be executed once.
    clientRef.current = new ApolloClient(/**/)
  }

  if (!clientRef.current) {
    // Now we have to wait until client is initialized with token, so you might want to add some spinner
    return null;
  }

  return <ApolloClient client={clientRef.current} >{children}</ApolloClient> 
}

And then you use it instead of original ApolloProvider in yours ReactDOM.render.

If token changes then things become a bit more difficult. https://www.apollographql.com/docs/react/networking/authentication/#header

You still use similar approach:

function useToken() { /* should return actual token and update it if changed */ }

function MyApolloProvider({ children }) {
  const token = useToken();
  const tokenRef = useRef(token);
  const clientRef = useRef(null);

  tokenRef.current = token; // make sure that tokenRef always has up to date token

  if (!clientRef.current) {
    // This function is called on each request individually and it takes token from ref
    const authLink = setContext((_, { headers }) => {
      // similar to documentation example, but you read token from ref
      const token = tokenRef.current;

      return {
        headers: {
          ...headers,
          authorization: `Bearer ${token}`,
        }
      }
    });

    clientRef.current = new ApolloClient(
      link: authLink.concat(httpLink),
      // ...
    )

  }
}

Usage in index.js regardless of first or second case:

ReactDOM.render(
  <Auth0Provider /* options */>
    <MyApolloProvider>
      {/* ... */}
    </MyApolloProvider>
  </Auth0Provider>,
  document.getElementById("root")
);

Main thing here is that MyApolloProvider should be within Auth0Provider to gain access to the token.

Crudden answered 25/1, 2021 at 14:6 Comment(2)
In the second exemple can you please complete the code? And how to use it in index. Js ?Sauerkraut
i tried the same approach but with different code and a cros problem appeared , i think with this code to i'll have the same problemSauerkraut

© 2022 - 2024 — McMap. All rights reserved.