如何识别 OAuth 令牌是否已过期?

我的 iOS 移动应用程序使用 OAuth2.0协议实现的服务。OAuth 访问令牌附带一个刷新令牌和一个 expires_in字段。我在应用程序中保存了刷新令牌和访问令牌过期时间,但是不知道什么时候使用它们。

  • 那么,使用这个 expires_in的通常和最佳实践是什么呢?
  • 如何识别我的访问令牌已过期?
  • 是否有一个常见的 Web 服务错误格式,表明我的访问令牌已过期?
135414 次浏览

下面是关于 OAuth 2.0令牌刷新的信息。

定义过期

OAuth 2.0标准 RFC 6749expires_in字段定义为到期的秒数:

Expires _ in: 推荐。访问令牌的生存期(以秒为单位)。例如,值“3600”表示访问令牌将在响应生成后一小时内过期。如果忽略,授权服务器应该通过其他方式提供过期时间或记录默认值。

令牌刷新处理: 方法1

在接收到有效的 access_tokenexpires_in值、 refresh_token等后,客户机可以通过存储过期时间并在每个请求上检查它来处理这个过期时间。这可以通过以下步骤实现:

  1. expires_in转换为过期时间(epoch、 RFC-3339/ISO-8601日期时间等)
  2. 保存过期时间
  3. 对于每个资源请求,根据过期时间检查当前时间,如果 access_token已过期,则在资源请求之前发出令牌刷新请求

一个示例实现是 Go oauth2库,它将令牌 expiry属性中的 expires_in值转换为 RFC3339日期时间。expiry不是由 OAuth 2.0标准定义的,但在这里很有用。

在检查时间时,确保您是相同的时间,例如,通过将所有时间转换为纪元或 UTC 时区使用相同的时区。

除了收到一个新的 access_token,您可能会收到一个新的 refresh_token与到期时间进一步在未来。如果收到这个命令,应该存储新的 refresh_token以延长会话的生命周期。

令牌刷新处理: 方法2

处理令牌刷新的另一种方法是在收到无效的令牌授权错误后手动刷新。这可以使用前面的方法完成,也可以单独完成。

如果您尝试使用过期的 access_token并且得到无效的令牌错误,您应该执行令牌刷新(如果您的刷新令牌仍然有效)。由于不同的服务可以对过期的令牌使用不同的错误代码,因此您可以跟踪每个服务的代码,或者一种简单的跨服务刷新令牌的方法是在遇到4xx 错误时尝试单次刷新。

无效访问令牌错误

下面是一些流行服务的错误代码:

  1. Facebook: 错误467无效访问令牌 -访问令牌已过期,已被撤销,或其他方面无效-处理过期访问令牌。
  2. LinkedIn: 错误401未授权。
  3. PayPal: 错误401未授权。

实施方案

Zapier 服务是一个实现授权后刷新错误重试的服务。

enter image description here

刷新令牌过期

如果您的 refresh_token也已过期,则需要再次进行授权过程。

OAuth 2.0规范没有定义刷新令牌过期或如何处理它,但是,当刷新令牌过期时,许多 API 将返回一个 refresh_token_expires_in属性。不同的 API 处理刷新令牌过期的方式不同,因此检查每个 API 的文档很重要,但通常在刷新访问令牌时可能会收到一个新的刷新令牌。过期应该以类似的方式处理,比如将 refresh_token_expires_in转换为 RFC3339日期时间 refresh_token_expiry值。

一些例子包括 领英易趣环中心。在 LinkedIn API 中,当刷新访问令牌时,您将收到一个刷新令牌,其 refresh_token_expires_in属性递减,目标是原始的刷新令牌到期时间,直到需要再次授权为止。RingCentral API 将返回带有静态时间的刷新令牌,这样,如果令牌刷新和刷新令牌更新一致,用户就不必再次进行身份验证。

建议使用上面的方法2,因为401可能由于多种原因发生,例如更新令牌签名证书或时钟差异:

  • 在每次 API 请求后检查401
  • 获得一个新的代币-仅此一次
  • 重试 API 请求——仅此一次

我已经实现了大量成功的 OAuth 客户端,并且一直使用这种技术——并且避免在我的客户端代码中读取 expires _ in 字段

这个问题指定了 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;
}