如何识别 OAuth 令牌是不是已过期?
Posted
技术标签:
【中文标题】如何识别 OAuth 令牌是不是已过期?【英文标题】:How to identify if the OAuth token has expired?如何识别 OAuth 令牌是否已过期? 【发布时间】:2015-08-29 20:38:30 【问题描述】:我的 ios 移动应用使用通过 OAuth2.0 协议实现的服务。 OAuth 访问令牌附带一个刷新令牌和一个expires_in
字段。我在我的应用中保存了刷新令牌和访问令牌过期时间,但不知道何时使用它们。
expires_in
的通常和最佳做法是什么?
如何识别我的访问令牌已过期?
是否有一种常见的 Web 服务错误格式显示我的访问令牌已过期?
【问题讨论】:
【参考方案1】:这是有关 OAuth 2.0 令牌刷新的信息。
定义过期
OAuth 2.0 标准 RFC 6749 将 expires_in
字段定义为到期秒数:
expires_in:推荐。访问令牌的生命周期(以秒为单位)。例如,值“3600”表示访问令牌将在响应生成后一小时内过期。如果省略,授权服务器应该通过其他方式提供过期时间或记录默认值。
令牌刷新处理:方法一
在收到有效的access_token
、expires_in
值、refresh_token
等后,客户端可以通过存储过期时间并在每个请求上检查它来处理此问题。这可以通过以下步骤完成:
-
将
expires_in
转换为过期时间(纪元、RFC-3339/ISO-8601 日期时间等)
存储过期时间
在每个资源请求上,检查当前时间与过期时间,如果access_token
已过期,则在资源请求之前发出令牌刷新请求
一个示例实现是 Go oauth2
库,它将 expires_in
值转换为令牌 expiry
property 中的 RFC 3339 日期时间。 expiry
不是由 OAuth 2.0 标准定义的,但在这里很有用。
检查时间时,请确保您是同一时间,例如,通过将所有时间转换为纪元或 UTC 时区来使用相同的时区。
除了收到一个新的access_token
之外,您将来可能还会收到一个新的refresh_token
,其到期时间会更长。如果您收到此邮件,您应该存储新的 refresh_token
以延长会话的生命周期。
令牌刷新处理:方法二
处理令牌刷新的另一种方法是在收到无效令牌授权错误后手动刷新。这可以通过以前的方法完成,也可以单独完成。
如果您尝试使用过期的access_token
并收到无效令牌错误,则应执行令牌刷新(如果您的刷新令牌仍然有效)。由于不同的服务可以对过期令牌使用不同的错误代码,因此您可以跟踪每个服务的代码,或者跨服务刷新令牌的简单方法是在遇到 4xx 错误时尝试一次刷新。
无效的访问令牌错误
以下是一些流行服务的错误代码:
-
Facebook: Error 467 Invalid access token - 访问令牌已过期、被撤销或无效 - 处理过期的访问令牌。
LinkedIn: Error 401 Unauthorized。
PayPal: Error 401 Unauthorized。
实现
Zapier服务是一种实现授权错误重试后刷新的服务。
刷新令牌过期
如果您的refresh_token
也已过期,则需要重新进行授权。
OAuth 2.0 spec 没有定义刷新令牌过期或如何处理它,但是,当刷新令牌过期时,许多 API 将返回 refresh_token_expires_in
属性。不同的 API 会以不同的方式处理刷新令牌过期,因此查看每个 API 的文档很重要,但通常您在刷新访问令牌时可能会收到一个新的刷新令牌。过期应该以类似的方式处理,例如将 refresh_token_expires_in
转换为 RFC 3339 日期时间 refresh_token_expiry
值。
一些示例包括LinkedIn、eBay 和RingCentral。在 LinkedIn API 中,当您刷新访问令牌时,您将收到一个带有递减 refresh_token_expires_in
属性的刷新令牌,该属性以原始刷新令牌到期时间为目标,直到您需要再次进行身份验证。 RingCentral API 将返回具有静态时间的刷新令牌,因此如果令牌刷新和刷新令牌更新一致完成,用户不必再次进行身份验证。
【讨论】:
您好,非常感谢您的回复!在查看过期时间时,如果用户更改了设备时间怎么办?它不会导致过于频繁地刷新访问令牌吗?关于检查无效令牌错误,您对 oAuth 服务器将返回的响应代码和错误格式有什么想法吗?所有 oAuth 服务器的错误响应是否相同? 经常发生的设备时间变化有哪些用例?时区更改应自动处理。由于标准中没有定义无效令牌错误代码,因此不同的服务选择了上面列出的不同错误代码。如前所述,一种简单的刷新方法可能是在出现 4xx 错误时尝试单次刷新。 Spotify 未经授权返回 401 聚会迟到了,但是您怎么知道refresh_token
是否无效? :O
IETF RFC 6749 没有定义刷新令牌过期或如何处理它,但是许多 API 实现了 refresh_token_expires_in
属性,我已经用这个信息更新了答案。【参考方案2】:
推荐上面的方法 2,因为 401 可能由于多种原因发生,例如更新令牌签名证书或时钟差异:
在每次 API 请求后检查 401 获取新令牌 - 仅一次 重试 API 请求 - 仅一次我已经实现了许多成功的 OAuth 客户端并且一直使用这种技术 - 并且避免在我的客户端代码中读取 expires_in 字段
【讨论】:
我打算在我的应用程序中实现代码以检查 expires_in 字段,但在时区和用户更改设备时间方面遇到了很多问题。所以我将按照你上面所说的方式实现我的 NetworkingClient。 令牌发行者返回的过期时间不应该与用户时区无关吗?我希望它是 UTC。【参考方案3】:该问题指定了 iOS,但作为任何工具集的一般原则,基于服务器的解决方案将令牌存储在服务器内存缓存中,并将缓存到期日期时间设置为与令牌的到期时间相同。
在需要身份验证令牌的任何其他端点之前调用以下函数。
这样它会从缓存中获取令牌,或者如果缓存过期(与令牌本身过期的时间相同)则获取新令牌。
对于 .NET:
private async Task<string> GetAuthToken()
string cacheKey = "AuthToken";
if (!_memoryCache.TryGetValue(cacheKey, out string authToken))
// Token not in cache, so get fresh one:
// Do call for token
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, *url*);
// Add Headers
...
// Make call
var response = await client.SendAsync(request);
string responseContent = await response.Content.ReadAsStringAsync();
// Check the call success/failure
if (!response.IsSuccessStatusCode)
return null;
JObject authObj = JObject.Parse(responseContent);
authToken = (string)authObj["access_token"];
string authTokenExpires = (string)authObj["expires_in"];
// Save data in cache.
MemoryCacheEntryOptions staticDataCacheMemoryOptions = new MemoryCacheEntryOptions()
// Keep in cache until expired by Provider
.SetAbsoluteExpiration(DateTime.Now.AddSeconds(Convert.ToInt32(authTokenExpires)));
_memoryCache.Set(cacheKey, authToken, staticDataCacheMemoryOptions);
return authToken;
【讨论】:
以上是关于如何识别 OAuth 令牌是不是已过期?的主要内容,如果未能解决你的问题,请参考以下文章