首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从react组件外部的auth0provider访问令牌

从react组件外部的auth0provider访问令牌
EN

Stack Overflow用户
提问于 2020-05-18 13:12:33
回答 6查看 6.6K关注 0票数 24

我正在使用用户在登录时提供的auth0令牌通过useAuth0.getTokenSilely进行api调用。

在本例中,fetchTodoListaddTodoItemupdateTodoItem都需要一个令牌来进行授权。我希望能够将这些函数提取到一个单独的文件中(比如utils/api-client.js并导入它们,而不必显式地传递令牌)。

代码语言:javascript
复制
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}
          />
        </>
      )}
    </>
  )
}

以下是回购程序的链接:https://github.com/gpspake/todo-client

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2020-05-19 23:12:25

好了,明白了!

现在我已经更好地理解了,我真正的问题是如何为axios请求提供一个auth0令牌,这样就不需要在组件中声明它们。

简短的回答是:初始化auth0时获取令牌,并注册一个axios拦截器,将该令牌设置为所有axios请求的标头值。

较长的答案(打字本中的例子):

声明一个接受令牌并注册axios拦截器的函数

代码语言:javascript
复制
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
  })
}

在auth0provider包装器中,初始化和验证auth0客户端时,获取带有setAxiosTokenInterceptor的令牌,并将其传递给注册拦截器的函数(从Auth0 React修改的示例):

代码语言:javascript
复制
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()
}, [])

在承诺得到解决时调用setLoading(false)可以确保,如果auth0完成加载,则拦截器已经注册。因为在完成auth0加载之前,不会呈现任何发出请求的组件,这将阻止在没有令牌的情况下进行任何调用。

这允许我将所有axios函数移动到一个单独的文件中,并将它们导入到需要它们的组件中。当调用这些函数时,拦截器将将令牌添加到标头utils/todo-client.ts中。

代码语言:javascript
复制
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
  }
}
...

关于github的完整来源

票数 3
EN

Stack Overflow用户

发布于 2021-07-16 10:16:57

关于如何在React组件之外使用getAccessTokenSilently,我遇到了一个类似的问题,最后我得出的结论是:

我的HTTP客户端包装器

代码语言:javascript
复制
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();
  }
}

在我的应用程序根目录上,我从getAccessTokenSilently传递auth0

代码语言:javascript
复制
 useEffect(() => {
    httpClient.setTokenGenerator(getAccessTokenSilently);
  }, [getAccessTokenSilently]);

就这样!

现在,您已经准备好了一个axios实例,可以使用

票数 8
EN

Stack Overflow用户

发布于 2021-01-24 19:45:48

这是@james快速回答的一个变体,在这里,我使用"RequestFactory“以axios格式生成请求,然后从Auth0添加auth头。

我也面临着同样的问题,通过将所有API调用逻辑移到我创建的自定义钩子中,我克服了这个限制:

代码语言:javascript
复制
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;

用法示例:

代码语言:javascript
复制
 import { RequestFactory } from 'api/requestFactory';

 const MyAwesomeComponent = () => {
   const { requestMaker } = useRequest(); // Custom Hook
   ...
   requestMaker(QueueRequestFactory.create(queueName))
     .then((response) => {
       // Handle response here
       ...
     });
 }

RequestFactory为我的不同API调用定义和生成请求有效负载,例如:

代码语言:javascript
复制
export const create = (queueName) => ({ method: 'post', url: '/queue', data: { queueName } });

这里是一个完整的Auth0集成PR,可供参考。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61870378

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档