oauth-2.0 - 使用 AD FS 4.0 (2016) 或更高版本获取新的刷新令牌
问题描述
我将 AD FS 2016 配置为支持通过 OAuth2/OpenID Connect 使用授权代码授予和 PKCE 对“本机应用程序”进行身份验证。我通过设置以下内容创建了一个依赖方并配置(用于测试目的)令牌生命周期:
Set-AdfsRelyingPartyTrust -TargetName MyRPT -IssueOAuthRefreshTokensTo AllDevices -TokenLifetime 3
Grant-AdfsApplicationPermission -ClientRoleIdentifier MyClient -ServerRoleIdentifier MyRPT -ScopeNames openid,profile,email
Set-AdfsProperty -SSOLifetime 7 -PersistenSsoEnabled $false
...这给了我在 3 分钟后过期的访问/ID 令牌和在 7(实际上是 14,见下文)分钟后过期的刷新令牌。我还禁用了持久 SSO,所以我没有得到会话 cookie。都好。
成功验证后,我的客户端向 /oauth2/token 端点发出 POST 请求,并传递以下参数:
- client_id:“我的客户”
- 代码: ...
- 重定向uri:...
- 代码验证器:...
- 授权类型:“授权代码”
我得到以下有效回复:
{
"access_token": "...",
"token_type": "bearer",
"expires_in": 180,
"resource": "MyRPT",
"refresh_token": "...",
"refresh_token_expires_in": 419,
"scope": "email profile openid",
"id_token": "..."
}
惊人的。
然后在访问令牌到期前大约 10 秒,客户端向 /oauth2/token 发出另一个 POST 请求,这次使用以下参数:
- 刷新令牌:...
- 授予类型:“刷新令牌”
- client_id:“我的客户”
并返回以下成功响应:
{
"access_token": "...",
"token_type": "bearer",
"expires_in": 180,
"id_token": "..."
}
请注意,这次没有返回刷新令牌。同样的情况又发生了四次(总共大约 14 分钟,所以是 SSOLifetime 的两倍?),而刷新令牌仍然有效,最后,在第四次请求新访问令牌时,我收到以下正文的 400 错误:
{
"error":"invalid_grant",
"error_description":"MSIS9615: The refresh token received in \u0027refresh_token\u0027 parameter has expired."
}
这有点道理,但是......当当前刷新令牌接近到期时间时,不应该发出新的刷新令牌吗?
官方文档对此事有些神秘:
尽管刷新令牌在用于获取新的访问令牌时不会被撤销,但您应该丢弃旧的刷新令牌。根据 OAuth 2.0 规范说:“授权服务器可以发布一个新的刷新令牌,在这种情况下,客户端必须丢弃旧的刷新令牌并用新的刷新令牌替换它。授权服务器可以在发布后撤销旧的刷新令牌客户端的新刷新令牌。” 当新的刷新令牌生命周期长于以前的刷新令牌生命周期时,AD FS 会发出刷新令牌。若要查看有关 AD FS 刷新令牌生命周期的其他信息,请访问 AD FS 单一登录设置。
错,什么?让我用伪代码写一下:
if (newRefreshTokenLifetime > previousRefreshTokenLifetime) {
issueNewRefreshToken();
}
...但那将永远是,不是吗?
关于如何配置 AD FS 以便它在需要时发布新的刷新令牌的任何想法?理想情况下,刷新令牌轮换会很好,但一次只有一件事......
解决方案
通常在 OAuth 中,刷新令牌的生命周期是在委托时设置的,当用户登录时,他们可能会同意在特定时间内使用某些权限。
因此,如果用户在 09:00 登录 8 小时会话,并且他们的应用程序在 10:00 刷新访问令牌,则如果发布了新的刷新令牌,则它应该可以使用 7 小时。也就是说,您不能在不再次涉及用户的情况下覆盖初始委托。
正如您所说,更新的趋势是在每次访问令牌刷新时获取一个新的刷新令牌,但这只是一种保护机制,ADFS 不支持。所以我将按照以下方式进行:
- 将 SSO Lifetime 设置为所需的值,例如 8 小时,并将访问令牌的生命周期设置为标准值,例如 30 分钟
- 以面向未来的方式编写代码,如果您获得新的刷新令牌,则丢弃现有的刷新令牌
在本机应用程序中刷新令牌
根据评论,您正在尝试使用 PKCE 并希望使用旋转刷新令牌,但 ADFS 不支持后者,因此您不能。
您有一个本机应用程序,其标准解决方案始终是将刷新令牌存储在仅适用于应用程序和用户的安全操作系统存储中。刷新令牌是否轮换无关紧要。例子:
SPA
令牌和浏览器是一个完全不同的话题,因为没有地方可以安全地存储刷新令牌。由于最近的第三方 cookie 浏览器限制,让 Javascript 应用程序工作的唯一方法是将刷新令牌存储在本地存储中,从安全角度来看,这是灾难性的。
这个棘手问题的最佳解决方案是使用 API 驱动的解决方案,其中实用程序 API 为 SPA 发出 SameSite=strict cookie。不过,有一些活动部件可以部署到开发人员 PC 和您的管道。有关设计模式的详细信息,请参阅以下 Curity 资源。这也适用于 ADFS。
推荐阅读
- android - 在输入 OTP 时键入后跳转到另一个 EditText 字段
- javascript - 将数组名称属性添加到 JSON 对象 [ES5]
- c# - 再次调用异步时取消任务c#
- javascript - 这个 if 语句有什么问题?(javascript)
- sql - 存储用户赞成/反对的帖子
- swift - 有没有办法从 SpriteView 场景中关闭 SwiftUI 视图?
- jboss - ActiveMQ 服务器在运行 1-3 天后在 JBoss EAP 7.2.8 中停止
- python - 我如何使用另一个类中的 Python 中的字典进行循环
- javascript - 如何使用 innerHTML 将从 localstorage 检索到的对象传递到 DOM 中?
- javascript - 使用循环将案例动态添加到开关