OAuth 2.0协议草案的第4.2节指出,授权服务器可以返回access_token(用于通过资源验证自己)和refresh_token,refresh_taken纯粹用于创建新的access_token:

https://www.rfc-editor.org/rfc/rfc6749#section-4.2

为什么两者都有?为什么不让access_token和refresh_token一样长,而不设置refresh_taken?


当前回答

为什么不让access_token和refresh_token一样长不吃点心吗?

除了其他人提供的出色答案之外,我们使用刷新令牌还有另一个原因,这与声明有关。

每个令牌都包含声明,这些声明可以包括用户名、用户角色或创建声明的提供者等任何内容。当刷新令牌时,这些声明将被更新。

如果我们更频繁地刷新令牌,显然会给我们的身份服务带来更大的压力;然而,我们正在获得更准确和最新的声明。

其他回答

为了理解这个问题的答案,我们需要了解两点。

第一点是,有时用户的访问令牌可能会在用户不知情的情况下被盗。由于用户不知道攻击,他们将无法手动通知我们。然后,就我们给攻击者完成攻击的时间(机会)而言,15分钟和一整天之间会有巨大的差异。因此,这就是我们需要每“短时间”(例如,每15分钟)“刷新”访问令牌的原因,我们不想将此操作推迟很长时间(例如,一整天)。因此,OP在问题中所说的显然不是一个选项(将访问令牌的到期时间延长到刷新令牌的有效时间)。

因此,我们至少还有以下两个选择:

要求每个用户每隔一段时间重新输入他们的凭据,以便为他们提供新的访问令牌。但显然,这不是一个流行的选项,因为它会困扰用户。使用刷新令牌。阅读下面的第二点,以了解其工作原理(背后的逻辑)。

第二点需要理解的是,因为我们已经将访问令牌与刷新令牌分开,现在可以以“不同的方式”发送刷新令牌,因此我们可以以攻击者的JavaScript(通常是客户端代码)无法访问的方式发送它,例如,使用httpOnly标记:

HttpOnly Cookie是添加到浏览器Cookie中的标记,用于阻止客户端脚本访问数据。来源

在生成cookie时使用HttpOnly标志有助于降低客户端脚本访问受保护cookie的风险。HttpOnly cookie于2002年由Microsoft Internet Explorer开发人员针对Internet Explorer 6 SP1首次实现。来源(感谢IE!)

因此,尽管攻击者仍然可以窃取访问令牌(强烈建议将其保存在RAM中,而不是本地存储等易受攻击的地方),但他们将无法窃取刷新令牌。因此,如果攻击者窃取了一个人的访问令牌,他们只有很短的时间滥用它(15分钟?比一整天好得多!),然后一旦它过期,他们就没有机会自己获得新的令牌。

据我所知,如果您需要撤销访问,刷新令牌只是为了性能和成本节省。

例1:不实现刷新令牌;仅实现长期访问令牌:如果用户滥用服务(例如:不支付订阅),您需要能够撤销访问令牌=>您需要在每个需要访问令牌的API调用上检查访问令牌的有效性,这会很慢,因为它需要DB查找(缓存可以帮助,但这更复杂)。

例2:实现刷新令牌和短期访问令牌:如果用户滥用服务(例如:不支付订阅),您需要能够撤销访问令牌=>短暂的访问令牌将在短暂的白色(例如1小时)后过期,用户将需要获得新的访问令牌,因此我们不需要对需要访问令牌的每个API调用进行验证。您只需要在从刷新令牌生成访问令牌时验证用户。对于坏用户,如果无法生成访问令牌,则可以注销该用户。当用户尝试重新登录时,验证将再次运行并返回错误。

刷新令牌的思想是,如果访问令牌因其短暂而受到破坏,攻击者可以在有限的窗口中滥用它。

刷新令牌(如果被破坏)是无用的,因为攻击者除了需要刷新令牌之外还需要客户端id和密码,以便获得访问令牌。

话虽如此,由于每次对授权服务器和资源服务器的调用都是通过SSL完成的,包括请求访问/刷新令牌时的原始客户端id和secret,因此我不确定访问令牌如何比长期刷新令牌和客户端id/secret组合更“不可妥协”。

当然,这与不同时控制授权服务器和资源服务器的实现不同。

这里有一个很好的线程,讨论了刷新令牌的使用:OAuth存档。

引用上述内容,讨论刷新令牌的安全目的:

刷新令牌。。。降低长期access_token泄漏的风险(在不安全的资源服务器上的日志文件中查询参数,测试版或编码不良的资源服务器应用程序,非https站点上的JS SDK客户端将access_toke放入cookie等)

Catchdave提供的讨论链接还有Dick Hardt提出的另一个有效的观点(原始的,死链接),我认为除了上面写的内容之外,这里还值得一提:

我记得刷新令牌是为了安全和撤销。<...>撤销:如果访问令牌是自包含的,则可以通过不发布新的访问令牌来撤销授权。资源不需要查询授权服务器以查看访问令牌是否有效。这简化了访问令牌验证,并使扩展和支持多个授权服务器更加容易。当访问令牌有效但授权被撤销时,有一个时间窗口。

事实上,在资源服务器和授权服务器是同一个实体的情况下,并且用户和其中任何一个之间的连接(通常)都是同样安全的,那么将刷新令牌与访问令牌分开并没有多大意义。

尽管如引用中所述,刷新令牌的另一个作用是确保用户可以随时(例如通过其配置文件中的web界面)撤销访问令牌,同时保持系统的可伸缩性。

通常,令牌可以是指向服务器数据库中特定记录的随机标识符,也可以包含自身的所有信息(当然,这些信息必须使用MAC进行签名)。

具有长期访问令牌的系统应该如何工作

服务器允许客户端通过发出令牌访问预定义范围内的用户数据。由于我们希望保持令牌的可撤销性,我们必须在数据库中存储令牌以及设置或取消设置的标志“已撤销”(否则,如何使用自包含令牌?)。数据库可以包含多达len(用户)x len(注册客户端)x len(范围组合)的记录。然后,每个API请求都必须命中数据库。虽然对执行O(1)的数据库进行查询非常简单,但单点故障本身可能会对系统的可伸缩性和性能产生负面影响。

具有长期刷新令牌和短期访问令牌的系统应该如何工作

在这里,我们发布两个密钥:带有数据库中相应记录的随机刷新令牌,以及签名的自包含访问令牌,其中包含过期时间戳字段。

由于访问令牌是自包含的,因此我们根本不必访问数据库来检查其有效性。我们所要做的就是解码令牌并验证签名和时间戳。

尽管如此,我们仍然需要保留刷新令牌的数据库,但对该数据库的请求数量通常由访问令牌的寿命定义(寿命越长,访问率越低)。

为了撤销特定用户对客户端的访问,我们应该将相应的刷新令牌标记为“已撤销”(或完全删除),并停止发布新的访问令牌。很明显,在一个窗口中,刷新令牌已被撤销,但其访问令牌可能仍然有效。

权衡取舍

刷新令牌部分消除了访问令牌数据库的SPoF(单点故障),但它们有一些明显的缺点。

“窗口”。事件“用户撤销访问”和“保证撤销访问”之间的时间间隔。客户端逻辑的复杂性。无刷新令牌使用访问令牌发送API请求如果访问令牌无效,则失败并要求用户重新验证带刷新令牌使用访问令牌发送API请求如果访问令牌无效,请尝试使用刷新令牌更新它如果刷新请求通过,则更新访问令牌并重新发送初始API请求如果刷新请求失败,请要求用户重新验证

我希望这个答案确实有意义,有助于某人做出更深思熟虑的决定。我还想指出,一些著名的OAuth2提供商,包括github和foursquare,采用了没有刷新令牌的协议,并对此感到满意。

让我们考虑一个系统,其中每个用户链接到一个或多个角色,每个角色链接到一种或多种访问权限。可以缓存这些信息以获得更好的API性能。但是,用户和角色配置可能会发生变化(例如,可能会授予新的访问权限,或者可能会撤销当前的访问权限),这些都应该反映在缓存中。

我们可以为此目的使用访问和刷新令牌。当使用访问令牌调用API时,资源服务器检查缓存的访问权限。如果有任何新的访问许可,则不会立即反映。一旦访问令牌过期(例如在30分钟内)并且客户端使用刷新令牌生成新的访问令牌,就可以使用来自DB的更新的用户访问权限信息来更新缓存。

换句话说,我们可以将昂贵的操作从使用访问令牌的每次API调用转移到使用刷新令牌生成访问令牌的事件。