【问题标题】:What is intent of ID Token expiry time in OpenID Connect?OpenID Connect 中 ID 令牌到期时间的意图是什么?
【发布时间】:2014-10-30 10:37:56
【问题描述】:

在 OpenID Connect 中,访问令牌 有一个到期时间。对于授权代码流,这通常很短(例如 20 分钟),之后您使用刷新令牌请求新的访问令牌。

ID 令牌 也有到期时间。我的问题是这样做的目的是什么?

任何小于刷新令牌到期时间的 ID 令牌到期时间都意味着您最终将拥有一个已过期的 ID 令牌,但是一个有效的访问令牌。

你的意思是:

  • 将您的 ID 令牌的有效期设置为比刷新令牌的有效期长,或者
  • 将其设置为与访问令牌相同的到期时间,并在到期时采取一些措施(什么?),或者
  • 只在收到时在您的客户端使用 ID 令牌,然后忽略过期时间?

OpenID Connect specification 只是说在验证 ID 令牌时,

"The current time MUST be before the time represented by the exp Claim."

其中(可能)支持上述第三个选项。


编辑

由于 OpenID Connect 建立在 OAuth2 之上,因此下面补充问题的答案可以在 OAuth2 specification 中找到,其中说:

expires_in
     RECOMMENDED.  The lifetime in seconds of the access token.

一个相关的问题是,当您为令牌交换授权码时,相同的规范说您可能会收到如下响应:

{
 "access_token": "SlAV32hkKG",
 "token_type": "Bearer",
 "refresh_token": "8xLOxBtZp8",
 "expires_in": 3600,
 "id_token": "eyJhbG[...]"
}

但是在这种情况下,“expires_in”与什么有关?访问令牌、刷新令牌还是 ID 令牌?

(有关信息,IdentityServer3 将此设置为访问令牌到期时间)。

【问题讨论】:

    标签: oauth-2.0 openid-connect


    【解决方案1】:

    我正在回答我自己的问题,因为我发现我的问题背后的一些假设是错误的,所以在这里更容易澄清,而不是重新编写问题。

    ID 令牌用于向客户证明用户已通过身份验证,以及他们的身份。

    当客户端接收到一个 ID 令牌时,它通常会执行一些操作,例如将其转换为 ClaimsIdentity,并将其持久化,例如使用 cookie。

    ID 令牌在使用时必须未过期(应该是,因为它刚刚被发布)。但是在此之后它不会再次使用,因此 它是否过期并不重要,而用户仍然有一个活动会话。客户端拥有它需要的身份验证信息,然后可以选择自己的策略来确定会话持续多长时间,然后用户必须再次登录。

    我在问这个问题时的错误假设是 ID 令牌和访问令牌应该一起使用,因此两者都需要有有效的到期日期。由于各种原因,这是错误的:

    • ID 令牌仅用于向客户端进行身份验证(如上所述)。
    • 访问令牌与客户端无关。它们用于访问资源,客户端仅在需要调用资源时才处理它们。
    • 像独立的 MVC 或 WebForms 应用程序只有需要一个 ID 令牌。如果它没有调用外部资源,则没有可以授予访问权限的内容,因此没有访问令牌。

    【讨论】:

    • 你有这方面的参考吗? Eugenio 声称您可以在他的回答中刷新 id 令牌。这是真的吗?
    • 您不能刷新 ID 令牌,因为它延长了它的到期时间(就像访问令牌可以通过使用离线访问令牌来刷新一样)。但是,如果您与 OpenID Connect Provider 有一个未过期的身份验证会话(例如登录 IdentityServer3 后的 cookie),那么当您重复登录请求时,Provider 可以跳过身份验证(因为 cookie 表明您已经完成了)并返回一个新的 ID 令牌(如果需要,还有访问令牌)。当然,这仅适用于 cookie 的生命周期比 ID 令牌更长的情况。
    • 虽然您可以这样做,但我不确定这样做是否正确。它对最终用户来说也不是无缝的,因为它需要一些浏览器重定向。
    • @Kir 如果您使用的是 Javascript 单页应用程序 (SPA),则访问令牌更新的第一次尝试通常是后台进程,因此最终用户不会被打断。例如,如果您的资源的 API 响应访问令牌已过期,则 SPA 会向身份服务器发出后台请求以获取新的访问令牌。仅当此操作失败(因为 ID 令牌已过期)时,您才必须要求用户再次登录。有关示例代码,请参阅github.com/IdentityServer/IdentityServer3.Samples/tree/master/… 的 JavascriptImplicitClient 示例。
    • @Nth.gol 当您从 IdP 请求访问令牌时,您指定“范围”参数。这可以包括 IdP 可能在用户信息端点上支持的“配置文件”和“电子邮件”等范围,所以是的,它可以保护 IdP 资源,如您所说。但是您也可以为您的资源(例如 API)指定范围,“api1, api2”,因此同样的访问令牌也用于保护这些资源。
    【解决方案2】:

    出于自己的原因,我不得不深入研究并写下来,所以我将在这里发布我学到的东西......

    首先,我将冒着明显的风险回答这个问题:如果当前时间大于过期时间,则 ID 令牌不能被信任并且其内容必须被忽略。提问者的回答指出,在用户初始认证后,ID Token 不再使用。但是,由于 ID 令牌是由身份提供者签名的,因此在任何时候提供一种可靠地确定用户是谁的其他服务的方法肯定会有用一个应用程序可能正在使用。使用简单的用户 ID 或电子邮件地址并不可靠,因为它很容易被欺骗(任何人都可以发送电子邮件地址或用户 ID),但由于 OIDC ID 令牌是由授权服务器签名的(这通常还具有作为第三方的好处)它不能被欺骗,并且是一种更可靠的身份验证机制。

    例如,移动应用程序可能希望能够告诉后端服务正在使用该应用程序的用户,并且它可能需要在初始身份验证后的短暂时间之后这样做,此时 ID Token 已过期,因此无法用于可靠地验证用户身份。

    因此,就像访问令牌(用于授权 - 指定用户拥有的什么权限)可以刷新一样,可以刷新ID Token(用于身份验证- 指定用户是)?根据 OIDC 规范,答案并不明显。在 OIDC/OAuth 中,有三个“流程”用于获取令牌,授权代码流程、隐式流程和混合流程(我将在下面跳过,因为它是其他两个的变体)。

    对于implicit flow in OIDC/OAuth,您通过将浏览器中的用户重定向到授权端点并包括id_token 作为response_type 请求参数的值,在授权端点请求ID 令牌。 Implicit Flow Successful Authentication Response 需要包含 id_token

    对于the Authentication Code flow,客户端在将用户重定向到授权端点时将code指定为response_type请求参数的值。成功的响应包括一个授权码。客户端客户端使用授权码向令牌端点发出请求,并且根据OIDC Core Section 3.1.3.3 Successful Token Response响应必须包含 ID 令牌

    因此,对于任一流程,这就是您最初获取 ID 令牌的方式,但您如何刷新它? OIDC Section 12: Using Refresh Tokens 有以下关于刷新令牌响应的声明:

    成功验证刷新令牌后,响应正文是第 3.1.3.3 节的令牌响应,但它可能不包含 id_token

    可能不包含 ID 令牌,并且由于没有指定强制它包含 ID 令牌的方法,因此您必须假设响应将不包含 ID 令牌。因此,从技术上讲,没有指定的方法可以使用刷新令牌“刷新”ID 令牌。因此,获取新 ID Token 的唯一方法是通过将用户重定向到授权端点来重新授权/验证用户并启动上述隐式流程或身份验证代码流。 OIDC 规范确实向 authorization request 添加了一个 prompt 请求参数,因此客户端可以请求授权服务器不使用任何 UI 提示用户,但重定向仍然必须发生。

    【讨论】:

    • 如果你正在编写一个通用的软件来使用任意的授权提供者,你不能依赖从刷新中返回 id_token。但是,如果您正在使用特定的提供者(例如 IdentityServer4),您可以检查它的能力,并使用刷新请求后收到的 id_token
    • 那么如何刷新id_token呢?
    • @jwilleke AFAIK,如上所述“获取新 ID 令牌的唯一方法是通过将用户重定向到授权端点来重新授权/验证用户”
    • @MichaelFreidgeim 有趣,您是指通过Open ID Connect Discovery mechanism 吗?我们究竟是如何做到的?
    • 关于“刷新的响应体可能不包含 id_token”的好答案。赞成。顺便说一句,我的理解是 OIDC 规范确实为使用 Refresh Token 获取新的 ID 令牌留下了余地:客户端可以通过将“id_token”指定为范围之一来做到这一点;但这里仍然需要注意一般性的注意事项,因为最终决定是否遵守您请求的范围是由身份验证服务器做出的。
    【解决方案3】:

    如果我理解正确,根据thisOpenID Connect Core 1.0 spec,ID 令牌本身可以存储在 cookie 中作为持久会话的机制,并与每个需要身份验证的请求一起发送给客户端。然后,客户端可以在本地或通过提供者的验证者端点(如果提供,like Google does)验证 ID 令牌。如果令牌过期,它应该发出另一个身份验证请求,除了这次在 URL 参数中使用prompt=none。还要确保在id_token_hint参数中发送过期的ID令牌,否则Provider可能会返回错误。

    因此,ID 令牌过期似乎很自然,但prompt=none 确保可以在没有用户干预的情况下顺利获取新的 ID 令牌(当然,除非用户退出该 OpenID)。

    【讨论】:

      【解决方案4】:

      目的相同:id_token 过期后不能使用。主要区别在于id_token 是一种数据结构,您无需调用任何服务器或端点,因为信息已编码在令牌本身中。常规的 access_token 通常是不透明的工件(如 GUID)。

      id_token 的消费者必须始终验证它的(时间)有效性。

      我对 IS 不是 100% 熟悉,但我猜它是一个便利领域。您应该始终检查exp 声明。

      过期只是验证之一。 id_tokens 也经过数字签名,这也是您必须执行的验证。

      【讨论】:

      • 谢谢欧金尼奥。我的主要问题是当 ID 令牌过期时你应该怎么做?我认为(可能是错误的)要更新一个短暂的访问令牌,您必须使用刷新令牌。但是,如果 ID-token 与 access-token 的有效期相同,您将立即拥有一个过期的 ID-token,因此刷新 access-token 似乎毫无意义。我想我可能在这里遗漏了一些东西!
      • 您将使用(未撤销的)refresh_token 来获取新的 access_token 或 id_token。或者干脆以用户身份再次登录。 id_tokens 在逻辑上等同于 access_tokens。只是格式不同。
      • 我的最新理解是,当用户与授权服务器进行经过身份验证的会话时,当访问令牌过期时,401 => 302 重定向到授权服务器将获得新的访问和 ID 令牌,而无需用户的干涉。但在离线模式下,refresh_token 只会返回一个新的 access_token,表示允许特定用户访问某些资源。它不能返回 id_token,因为这表示特定用户已通过身份验证,但在离线模式下并非如此。
      • 对于关于 id_token 和 access_token 之间有什么区别的问题(尤其是在使用不透明/参考令牌时),这将是一个很好的答案。重点先回答问题,再搞清楚access token和id token是怎么用的?
      【解决方案5】:

      刷新令牌意味着即使用户未登录,您也可以再次使用它从授权服务器(在本例中为 OP - OpenID-Connect 提供程序)请求某些内容。您通常仅在有限的资源上允许此操作,并且仅在用户登录并至少经过一次身份验证后才允许。刷新令牌本身也应该有时间限制。

      在 OIDC 隐式流中,您调用授权端点,
      并在响应中接收 ID 令牌以及所有范围以及其中的所有声明信息。
      对 API 的后续调用应通过代码流来完成。
      隐式流旨在启用仅 javascript 或仅浏览器应用程序。不是与服务器交互的应用程序。
      因此,即使有一种方法可以“刷新”这个令牌,你也不应该 - 出于安全考虑 - 让它的存在时间过长。它将被冒充该 id 的未经授权的用户窃取和重复使用。您应该为此强制重新登录。

      代码流中,您调用 OP 的授权端点,并接收 授权码(也称为授权令牌,或简称 authcode)。这应该与您在隐式流程中收到的 id_token 类似,出于相同的原因并且不能也不应该更新。

      然后,您的 UI 或应用程序调用 OP 的 Token 端点,并接收(有时在用户通过 UI 进一步同意允许在 OP 的服务器上使用其拥有的资源之后):

      • 一个 id_token,用于身份验证 - 永远不要在服务器调用中再次使用它,除非在注销期间作为提示,当它的到期不再重要时,因此,由于上述原因,应该让其到期,并且永远不会神清气爽。
      • access_token - 稍后,在调用 API 时,可以将其提供给 OP 的 UserInfo 端点。这将返回声明,API 可以相应地进行授权。

      您可以刷新此 access_token,因为它仅告诉 API 用户拥有哪些声明,以及用户同意向您提供哪些资源(按范围和每个范围的声明)。如上所述,这是为了即使在用户不再登录后也允许访问。当然,您绝不希望允许刷新 id_token,因为您不希望在不登录的情况下允许模拟。

      【讨论】:

      • 你所说的关于隐式流的部分是不正确的。使用隐式流的客户端可以获得除了 ID 令牌之外的访问令牌,并且可以使用该访问令牌与服务器交互。
      • 有一种常见的做法,就是当 id_token 过期时,客户端向服务器请求新的令牌,这样用户就不需要再次授权了。例如。见damienbod.com/2017/06/02/…
      【解决方案6】:

      我想将此答案作为评论发布,但由于我在 StackOverflow 上不太活跃,我想我将其作为替代答案发布。

      在尝试将用户从会话http://openid.net/specs/openid-connect-session-1_0.html 中注销时,您还可以使用id_token 作为id_token_hint。老实说,我认为 id_token 此时是否过期并不重要,因为您只关心注销特定用户。

      【讨论】:

        【解决方案7】:

        TLDR;

        在信任它所说的内容之前验证 ID 令牌。

        更多详情

        OpenID Connect 中 ID 令牌到期时间的意图是什么?

        意图是允许客户端验证 ID 令牌,并且客户端必须在使用 ID 令牌信息的操作之前验证 ID 令牌

        来自the OpenID Implicit Flow spec

        如果本文档中定义的任何验证程序失败,则必须中止任何需要未能正确验证的信息的操作,并且不得使用未能验证的信息。

        为了证实这一点,Google's OpenID Connect documentation 谈到了 ID 令牌验证:

        使 ID 令牌有用的一件事是,您可以在应用程序的不同组件中传递它们。这些组件可以使用 ID 令牌作为对应用程序和用户进行身份验证的轻量级身份验证机制。 但在您可以使用 ID 令牌中的信息或将其作为用户已通过身份验证的断言之前,您必须对其进行验证。

        因此,如果我们的客户端应用程序要根据 ID 令牌的内容采取一些行动,那么我们必须再次验证 ID 令牌。

        【讨论】:

          【解决方案8】:

          只是分享我的旅程。现在是 2021 年 6 月。我写这篇文章是因为我偶然发现了 3rd-party 身份验证业务。我是一名资深程序员,但对安全性很陌生。换句话说,所有的标准、规范和术语都是陌生的,任何人都可以在这个领域击败我。请原谅我没有遵守所有条款。

          切入正题,我正在编写一个 Angular/Node 应用程序,所以 UI=Angular,API(API 服务器)=Node/Express。我没有创建自己的用户名/密码身份验证,而是转向第三方身份验证,让他们验证用户声称的真实性。这里有两本对我来说很重要的指南:

          1. Angular Authentication With JSON Web Tokens (JWT): The Complete Guide
          2. Eiji's Authenticate with a backend server

          结合 No. 1 和 angularx-social-login,我将 UI 连接到 Google,然后将 idToken 附加到 API,vola!继2号使用本地库API可以验证idToken,太棒了!

          等等,idTokenexp 将在 1 小时后过期。如何刷新?

          我的理解是我所需要的只是谷歌的身份验证,我不在乎他们使用什么标准和版本,但和其他人一样,我只相信他们的身份验证。 身份验证基本上验证他们声称自己是谁。 授权是对用户可以做什么/在哪里执行/转到的访问控制。内部访问控制(允许用户做什么)没有暴露给谷歌,他们不知道。所以accessToken 应该不在图片中。对吧?

          我花了几天时间研究如何刷新idToken,现在得出的结论是 Google 不推荐它,angularx-social-login 也没有提供方法。 2号中Eiji has stated clearly

          因此,我的情况的解决方案是

          • 使用 Google 的身份验证。
          • 在初始验证 idToken 后在 API 上创建自己的会话管理/超时规则以缓解 exp
          • 最好使用res.cookie("SESSIONID", myOwnID, {httpOnly:true, secure:true});将此会话数据添加到cookie中

          为了更好的保护,Eiji 也推荐Cross Account Protection。希望这会对某人有所帮助!

          【讨论】:

            猜你喜欢
            • 2018-04-16
            • 2015-06-27
            • 1970-01-01
            • 2014-09-12
            • 2017-11-12
            • 2019-04-15
            • 2021-07-25
            • 2023-03-18
            相关资源
            最近更新 更多