Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Auth0's hook useAuth0 to get token and set header in Apollo client

Hello I'm trying to use Auth0's spa example with react and I'm using the useAuth0 hook, I'm also using Apollo client to make my queries and I need to get the token and set it in the request, however I haven't been able to set it.

I would be very grateful if someone could point me in the right direction.

I've tried to use the context property in the query/mutation component but I couldn't figure it out nor find information about how to use it.

like image 356
Manuel Morales Avatar asked Aug 07 '19 17:08

Manuel Morales


2 Answers

I had the same dilemma, especially since the Auth0 hook can only be used from within a functional component but the docs seem to set up the ApolloProvider in the index file.

With a bit of experimentation I managed to get around this by creating a wrapper component which allows me to make use of the useAuth0 hook and asynchronously fetch/attach the token to each request.

I created a new file AuthorizedApolloProvider.tsx:

import { ApolloClient, ApolloProvider, createHttpLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/link-context';
import React from 'react';

import { useAuth0 } from '../react-auth0-spa';

const AuthorizedApolloProvider = ({ children }) => {
  const { getTokenSilently } = useAuth0();

  const httpLink = createHttpLink({
    uri: 'http://localhost:4000/graphql', // your URI here...
  });

  const authLink = setContext(async () => {
    const token = await getTokenSilently();
    return {
      headers: {
        Authorization: `Bearer ${token}`
      }
    };
  });

  const apolloClient = new ApolloClient({
    link: authLink.concat(httpLink),
    cache: new InMemoryCache(),
    connectToDevTools: true
  });

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

export default AuthorizedApolloProvider;

Then in my index.tsx file I wrap App with my new AuthorizedApolloProvider instead of using ApolloProvider directly.

ReactDOM.render(
  <Auth0Provider
    domain={config.domain}
    client_id={config.clientId}
    redirect_uri={window.location.origin}
    audience={config.audience}
    onRedirectCallback={onRedirectCallback}>

      <AuthorizedApolloProvider>
        <App />
      </AuthorizedApolloProvider>

  </Auth0Provider>,  
  document.getElementById('root')
);

Note: The above example is using Apollo Client 3 beta, and I had to install @apollo/link-context in addition to @apollo/client. I guess the required imports might be different for versions of Apollo Client.

like image 119
Matt Wilson Avatar answered Sep 20 '22 17:09

Matt Wilson


The way I tackled this issue is by editing an article I found online from https://hasura.io/

In other words, it uses react's useContext() hook and useEffect() to check and get the jwt token by using auth0's getTokenSilently() function.

I will just write the parts that are relevant:

import React, { FC, ReactNode } from 'react'
import { useAuth0 } from '@auth0/auth0-react'
import { ApolloProvider } from 'react-apollo'
import { ApolloClient, HttpLink, InMemoryCache } from 'apollo-boost'
import { setContext } from 'apollo-link-context'
import { useState, useEffect } from 'react'

const httpLink = new HttpLink({
  uri: 'yourdomain.test/graphql',
})

const Page: FC<{}> = ({children }) => {
  const [accessToken, setAccessToken] = useState('')
  const [client, setClient] = useState() as [ApolloClient<any>, any] // that could be better, actually if you have suggestions they are welcome
  const { getAccessTokenSilently, isLoading } = useAuth0()

  // get access token
  useEffect(() => {
    const getAccessToken = async () => {
      try {
        const token = await getAccessTokenSilently()
        setAccessToken(token)
      } catch (e) {
        console.log(e)
      }
    }
    getAccessToken()
  }, [])

  useEffect(() => {
    const authLink = setContext((_, { headers }) => {
      const token = accessToken
      if (token) {
        return {
          headers: {
            ...headers,
            authorization: `Bearer ${token}`,
          },
        }
      } else {
        return {
          headers: {
            ...headers,
          },
        }
      }
    })

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

    setClient(client)
  }, [accessToken])

  if (!client) {
    return <h1>Loading...</h1>
  }

  return (
    <ApolloProvider client={client}>
      {...children}
    </ApolloProvider>
  )
}
like image 22
Mel Macaluso Avatar answered Sep 22 '22 17:09

Mel Macaluso