从现有的 id_token 获取 Auth0 access_token

Posted

技术标签:

【中文标题】从现有的 id_token 获取 Auth0 access_token【英文标题】:Get Auth0 access_token from existing id_token 【发布时间】:2017-04-04 23:43:48 【问题描述】:

我正在使用auth0 来验证我对单页应用程序(基于 React 构建)的登录。我主要使用基本 API 调用(列出 here)。

我正在使用的流程是:

当用户在我的应用程序的登录页面上输入它们时获取用户名/电子邮件和密码 使用这些值向/oauth/ro 发送 POST 请求 - 这是代码:

export const login = (params, err) => 
  if (err) return err
  const email, password = params
  const AUTH0_CLIENT_ID, AUTH0_DOMAIN = process.env
  return fetch(`$AUTH0_DOMAIN/oauth/ro`, 
    method: 'POST',
    headers: 
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    ,
    body: JSON.stringify(
      'client_id': AUTH0_CLIENT_ID,
      'username': email,
      'password': password,
      'connection': 'Username-Password-Authentication',
      'grant_type': 'password',
      'scope': 'openid',
      'device': '',
      'id_token': ''
    )
  )
  .then(response => response.json())
  .then(json => 
    const id_token, access_token = json
    setCookieValue('id_token', id_token) // utility function I wrote
    return getProfile(access_token)
      .then(data => 
        const user_id, email: emailAddress, picture, name = data
        return id_token, user_id, emailAddress, picture, name
      )
  )
  .catch(error => console.log(`ERROR: $error`))

这都是通过 Redux 发送的,并且用户已经登录(假设用户名/密码正确)。

但是,我试图弄清楚在刷新页面/返回应用程序时如何保持登录。我将id_token(这是一个JWT)保存在浏览器的cookies中,并且可以在应用程序呈现服务器端时获取它。我可以解码 JWT 并获取有效负载(sub 是来自 auth0 的用户 ID)。但是,要获取配置文件数据,我需要 Auth0 在使用 /oauth/ro POST 请求时提供的 access_token。显然,如果 JWT 令牌已过期,那么它只会拒绝它并让用户退出。

这是我解码 JWT 的代码(发生在应用程序渲染上):

const ID_TOKEN = req.cookies.id_token || false
if (ID_TOKEN) 
  verifyJwt(ID_TOKEN, (err, decoded) => 
    if (err)  console.log(`JWT Verification error: $err`) 
    else 
      const sub = decoded
      getProfile(sub).then(data => store.dispatch(fetchUserDetails(data))) // fails as `sub` (the user id) is not the `access_token` which it requires
    
  )

我尝试再次使用/oauth/ro 调用,但这次指定"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer" 并使用从cookie 中检索到的id_token,并指定device。但是,当我进行此调用时,我从 Auth0 收到此错误:


  "error": "invalid_request",
  "error_description": "there is not an associated public key for specified client_id/user_id/device"

所以我的问题是,我需要进行什么 API 调用才能从 id_token JWT 获取 access_token

另外,作为奖励 - 当我发出 POST 请求登录时,password 正在通过纯文本传输。发送到 auth0 时如何加密它,以便他们可以解密回来?我认为它涉及使用 auth0 提供的client_secret,但我不知道该怎么做。

【问题讨论】:

【参考方案1】:

通过使用refresh tokens,无需任何类型的用户交互即可以编程方式刷新令牌。但是,这不适用于基于浏览器的应用程序,因为刷新令牌是长期存在的凭据,并且浏览器的存储特性会使它们面临更大的泄露风险。

如果您想继续使用资源所有者密码凭证授权,您可以选择在令牌过期时要求用户再次输入凭证。作为替代方案,在进行身份验证后,您可以获得所需的用户信息并启动特定于应用程序的会话。这可以通过让您的服务器端逻辑创建应用程序特定的会话标识符或 JWT 来实现。

您还可以停止使用资源所有者密码凭据授予并将用户重定向到 Auth0 身份验证页面,该页面除了将令牌返回给您的应用程序外,还将为用户维护一个经过身份验证的会话,这意味着当令牌过期并且您的应用程序再次重定向到 Auth0,用户可能不需要手动重新输入凭据,因为 Auth0 会话仍然有效。

关于以明文形式发送的密码;资源所有者端点依赖于 HTTPS,因此数据在协议级别进行加密。您还必须在自己的应用程序中使用 HTTPS 进行任何类型的通信,包括任何类型的用户凭据。

另外请注意,您可以通过使用范围来控制 ID 令牌中返回的内容,具体取决于相关信息量,如果您使用signal that you want that information to be contained within the ID token itself,您甚至可能不需要进行额外调用来获取用户配置文件。

【讨论】:

您好 Joao,感谢您的详细回答。我已经阅读了有关刷新令牌以及它们不应该如何存储在浏览器中的信息,因为它们是持久的并且如果它们落入坏人之手就会出现安全漏洞。但是,我不是我需要使用哪个 API 调用从 auth0 获取 refresh_token(我真的不想将我的用户重定向到像 /authorize 这样的 Auth0 登录页面)。我可能暂时离开它——它只是一个 MVP,所以如果用户每次都需要重新登录也没关系。谢谢! 鉴于您了解后果,我不会因为向您展示如何操作而感到难过... :) 您可以通过资源所有者端点获取刷新令牌,方法是将 offline_access 包含为范围,然后另外传递一个带有任意值的device 参数。我发布的链接显示了如何使用另一个端点执行此操作,但您也将它应用到 ro 一个。 这真的很有帮助,谢谢!当我用/oauth/ro 尝试这个时,(任意device 值)它给了我上面的错误("error_description": "there is not an associated public key for specified client_id/user_id/device")。我包括了id_token 并更改了grant_type(在问题中的上面的字符串)。我该如何解决这个错误? 您需要执行我在使用用户名/密码和password 授权类型调用ro 时提到的步骤。除了您获得的其他令牌之外,响应还将包括一个刷新令牌。

以上是关于从现有的 id_token 获取 Auth0 access_token的主要内容,如果未能解决你的问题,请参考以下文章

CefSharp 从现有的 Javascript 函数中获取结果

如何使用解析服务器从现有的 mongo 集合中获取数据?

从现有的 SwiftUI @States 派生绑定

无法使用 auth0 锁定登录

Auth0 /authorize 端点不返回 JWT

在 ASP.NET Core API 中处理 id_token