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
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.
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.
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 ):
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With