Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

access token from auth0provider outside of react components

I'm using the auth0 token provided by the user on login to make api calls via useAuth0.getTokenSilently.

In this example, fetchTodoList, addTodoItem, and updateTodoItem all require a token for authorization. I'd like to be able to extract these functions out in to a separate file (like utils/api-client.js and import them without having to explicitly pass in the token.

import React, { useContext } from 'react'
import { Link, useParams } from 'react-router-dom'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCircle, faList } from '@fortawesome/free-solid-svg-icons'
import axios from 'axios'
import { queryCache, useMutation, useQuery } from 'react-query'
import { TodoItem } from '../models/TodoItem'
import { TodoInput } from './TodoInput'
import { TodoList as TodoListComponent } from './TodoList'
import { TodoListsContext } from '../store/todolists'
import { TodoListName } from './TodoListName'
import { TodoList } from '../models/TodoList'
import { useAuth0 } from '../utils/react-auth0-wrapper'

export const EditTodoList = () => {

  const { getTokenSilently } = useAuth0()

  const fetchTodoList = async (todoListId: number): Promise<TodoList> => {
    try {
      const token = await getTokenSilently!()

      const { data } = await axios.get(
        `/api/TodoLists/${todoListId}`,
        {
          headers: {
            Authorization: `Bearer ${token}`
          }
        }
      )
      return data
    } catch (error) {
      return error
    }
  }

  const addTodoItem = async (todoItem: TodoItem): Promise<TodoItem> => {
    try {
      const token = await getTokenSilently!()

      const { data } = await axios.post(
        '/api/TodoItems',
        todoItem,
        {
          headers: {
            Authorization: `Bearer ${token}`,
          }
        }
      )
      return data
    } catch (addTodoListError) {
      return addTodoListError
    }
  }

  const updateTodoItem = async (todoItem: TodoItem) => {
    try {
      const token = await getTokenSilently!()

      const { data } = await axios.put(
        '/api/TodoItems',
        todoItem,
        {
          headers: {
            Authorization: `Bearer ${token}`,
          }
        }
      )
      return data
    } catch (addTodoListError) {
      return addTodoListError
    }
  }

  const [updateTodoItemMutation] = useMutation(updateTodoItem, {
    onSuccess: () => {
      queryCache.refetchQueries(['todoList', todoListId])
    }
  })

  const [addTodoItemMutation] = useMutation(addTodoItem, {
    onSuccess: () => {
      console.log('success')
      queryCache.refetchQueries(['todoList', todoListId])
    }
  })

  const onAddTodoItem = async (todoItem: TodoItem) => {
    try {
      await addTodoItemMutation({ 
        ...todoItem, 
        todoListId: parseInt(todoListId, 10) 
      })
    } catch (error) {
      // Uh oh, something went wrong
    }
  }

  const { todoListId } = useParams()
  const { status, data: todoList, error } = useQuery(['todoList', todoListId], () => fetchTodoList(todoListId))
  const { todoLists, setTodoList } = useContext(TodoListsContext)
  const todoListIndex = todoLists.findIndex(
    list => todoListId === list.id.toString()
  )

  const setTodoItems = (todoItems: TodoItem[]) => {
    // if(todoList) {
    //   const list = { ...todoList, todoItems }
    //   setTodoList(todoListIndex, list)
    // }
  }

  const setTodoListName = (name: string) => {
    // setTodoList(todoListIndex, { ...todoList, name })
  }

  return (
    <>
      <Link className="block flex align-items-center mt-8" to="/">
        <span className="fa-layers fa-fw fa-3x block m-auto group">
          <FontAwesomeIcon 
            icon={faCircle} 
            className="text-teal-500 transition-all duration-200 ease-in-out group-hover:text-teal-600" 
          />
          <FontAwesomeIcon icon={faList} inverse transform="shrink-8" />
        </span>
      </Link>

      {status === 'success' && !!todoList && (
        <>
          <TodoListName
            todoListName={todoList.name}
            setTodoListName={setTodoListName}
          />
          <TodoInput 
            onAddTodoItem={onAddTodoItem} 
          />

          <TodoListComponent
            todoItems={todoList.todoItems}
            setTodoItems={setTodoItems}
            updateTodo={updateTodoItemMutation}
          />
        </>
      )}
    </>
  )
}

Here's a link to the repo: https://github.com/gpspake/todo-client

like image 512
ItsGeorge Avatar asked May 18 '20 13:05

ItsGeorge


People also ask

How do I get the access token for a react API?

In the case of your APIs, you'll define custom API scopes to implement access control, and you'll identify them in the calls that your client applications make to that API. Once you configure Auth0Provider, you can easily get the access token using the getAccessTokenSilently () method from the useAuth0 () custom React Hook wherever you need it.

How does Auth0 work with react API?

As Auth0 can only issue tokens for custom scopes that exist on your API, ensure that you define the scopes used above when setting up an API with Auth0. Auth0 uses the value of the audience prop to determine which resource server (API) the user is authorizing your React application to access.

How to get the token interceptor from the Auth0 client?

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

How do I call a protected API from react-router?

See examples for react-router , Gatsby and Next.js. To call a protected API with an Access Token, be sure to specify the audience and scope of your access token, either in Auth0Provider or getAccessTokenSilently. Then use it to call a protected API by passing it in the Authorization header of your request.


1 Answers

I was having a similar issue on how to use getAccessTokenSilently outside of a React component, what I ended up with was this:

My HTTP client wrapper

export class HttpClient {
  constructor() {
    HttpClient.instance = axios.create({ baseURL: process.env.API_BASE_URL });

    HttpClient.instance.interceptors.request.use(
      async config => {
        const token = await this.getToken();

        return {
          ...config,
          headers: { ...config.headers, Authorization: `Bearer ${token}` },
        };
      },
      error => {
        Promise.reject(error);
      },
    );

    return this;
  }

  setTokenGenerator(tokenGenerator) {
    this.tokenGenerator = tokenGenerator;
    return this;
  }

  getToken() {
    return this.tokenGenerator();
  }
}


On my App root, I pass the getAccessTokenSilently from auth0

 useEffect(() => {
    httpClient.setTokenGenerator(getAccessTokenSilently);
  }, [getAccessTokenSilently]);

And that's it!

You now have an axios instance ready to do authenticated requests with

like image 153
Nélson Costa Avatar answered Sep 25 '22 07:09

Nélson Costa