access token from auth0provider outside of react components access token from auth0provider outside of react components reactjs reactjs

access token from auth0provider outside of react components


I'm not exactly sure why you couldn't access the token inside of your individual functions? Is it because they wouldn't be React function components but just regular functions?

One of the things I have done is create a useFetch hook that can get the user token and attach it to a request itself. Then, instead of exporting those functions specifically,I can just call this new fetch hook. Here's an example of what I mean.

import React from "react"import { useAuth0 } from "../utils/auth"const useFetch = () => {  const [response, setResponse] = React.useState(null)  const [error, setError] = React.useState(null)  const [isLoading, setIsLoading] = React.useState(false)  const { getTokenSilently } = useAuth0()  const fetchData = async (url, method, body, authenticated, options = {}) => {    setIsLoading(true)    try {      if (authenticated) {        const token = await getTokenSilently()        if (!options.headers) {          options.headers = {}        }        options.headers["Authorization"] = `Bearer ${token}`      }      options.method = method      if (method !== "GET") {        options.body = JSON.stringify(body)      }      const res = await fetch(url, options)      const json = await res.json()      setResponse(json)      setIsLoading(false)      if (res.status === 200) {        return json      }      throw { msg: json.msg }    } catch (error) {      console.error(error)      setError(error)      throw error    }  }  return { response, error, isLoading, fetchData }}export default useFetch


Ok, got it!

Now that I understand better, my real question was how to provide an auth0 token to axios requests such that they don't need to be declared in components.

The short answer:Get the token when auth0 is initialized and register an axios interceptor to set that token as a header value for all axios requests.

The long answer (examples in typescript):

Declare a function that takes a token and registers an axios interceptor

const setAxiosTokenInterceptor = async (accessToken: string): Promise<void> => {  axios.interceptors.request.use(async config => {    const requestConfig = config    if (accessToken) {      requestConfig.headers.common.Authorization = `Bearer ${accessToken}`    }     return requestConfig  })}

In the auth0provider wrapper, when the auth0 client is initialized and authenticated, get the token with setAxiosTokenInterceptor and pass it to the function that registers the interceptor (Modified example from the Auth0 React SDK Quickstart):

useEffect(() => {    const initAuth0 = async () => {        const auth0FromHook = await createAuth0Client(initOptions)        setAuth0(auth0FromHook)        if (window.location.search.includes('code=')) {            const { appState } = await auth0FromHook.handleRedirectCallback()            onRedirectCallback(appState)        }        auth0FromHook.isAuthenticated().then(            async authenticated => {                setIsAuthenticated(authenticated)                if (authenticated) {                    auth0FromHook.getUser().then(                        auth0User => {                            setUser(auth0User)                        }                    )                    // get token and register interceptor                    const token = await auth0FromHook.getTokenSilently()                    setAxiosTokenInterceptor(token).then(                        () => {setLoading(false)}                    )                }            }        )    }    initAuth0().catch()}, [])

Calling setLoading(false) when the promise is resolved ensures that, if auth0 is finished loading, the interceptor has been registered. Since none of the components that make requests are rendered until auth0 is finished loading, this prevents any calls from being made without the token.

This allowed me to move all of my axios functions in to a separate file and import them in to the components need them. When any of these functions are called, the interceptor will add the token to the headerutils/todo-client.ts

import axios from 'axios'import { TodoList } from '../models/TodoList'import { TodoItem } from '../models/TodoItem'export const fetchTodoLists = async (): Promise<TodoList[]> => {  try {    const { data } = await axios.get(      '/api/TodoLists'    )    return data  } catch (error) {    return error  }}export const fetchTodoList = async (todoListId: number): Promise<TodoList> => {  try {    const { data } = await axios.get(      `/api/TodoLists/${todoListId}`    )    return data  } catch (error) {    return error  }}export const addTodoItem = async (todoItem: TodoItem): Promise<TodoItem> => {  try {    const { data } = await axios.post(      '/api/TodoItems',      todoItem    )    return data  } catch (addTodoListError) {    return addTodoListError  }}...

Full source on github


This is a variant of the answer by @james-quick, where I am using a "RequestFactory" to generate requests in the axios format, and then just adding the auth header from Auth0

I was facing the same issue, and I got around this limitation by moving all my API call logic into a custom hook that I created:

import { useAuth0 } from '@auth0/auth0-react';import { useCallback } from 'react';import makeRequest from './axios';export const useRequest = () => {  const { getAccessTokenSilently } = useAuth0();  // memoized the function, as otherwise if the hook is used inside a useEffect, it will lead to an infinite loop  const memoizedFn = useCallback(    async (request) => {      const accessToken = await getAccessTokenSilently({ audience: AUDIANCE })      return makeRequest({        ...request,        headers: {          ...request.headers,          // Add the Authorization header to the existing headers          Authorization: `Bearer ${accessToken}`,        },      });    },    [isAuthenticated, getAccessTokenSilently]  );  return {    requestMaker: memoizedFn,  };};export default useRequest;

Usage Example:

 import { RequestFactory } from 'api/requestFactory'; const MyAwesomeComponent = () => {   const { requestMaker } = useRequest(); // Custom Hook   ...   requestMaker(QueueRequestFactory.create(queueName))     .then((response) => {       // Handle response here       ...     }); }

RequestFactory defines and generates the request payload for my different API calls, for example:

export const create = (queueName) => ({ method: 'post', url: '/queue', data: { queueName } });

Here is a full Auth0 integration PR for reference.