基于IdentityServer4实现类似Facebook的强制登出其他设备求助
首先,你遇到的情况是正常的,因为授权端点(/connect/authorize)本身并不验证refresh_token的存在与否。让我拆解一下原因和解决方案:
为什么删除refresh_token后仍能获取新token?
当用户的access_token过期后,MVC客户端重定向到IdentityServer的授权端点时,此时验证的是用户在IdentityServer上的会话状态,而不是refresh_token。只要用户在IdentityServer还有有效的登录会话(比如浏览器里的IdentityServer cookie没过期),IdentityServer就会直接返回授权码,后续客户端用授权码换取新的access_token和refresh_token——这个流程完全不依赖之前被删除的那个refresh_token。
删除refresh_token只是让该设备无法通过/connect/token的refresh_token流程刷新token,但只要用户的IdentityServer会话还活着,走授权流程依然能拿到新的凭证。
正确实现强制登出其他设备的步骤
要实现类似Facebook的强制登出其他设备,需要同时处理IdentityServer的用户会话和所有关联的refresh_token,具体步骤如下:
1. 撤销用户在IdentityServer的所有会话
IdentityServer4提供了ISessionService来管理用户会话,你可以调用它的RevokeUserSessionsAsync方法,撤销指定用户的所有登录会话。这样其他设备上的用户会话会立即失效,下次访问时必须重新登录。
示例代码(在Auth服务器的API中实现):
private readonly ISessionService _sessionService; private readonly IRefreshTokenService _refreshTokenService; public DeviceController(ISessionService sessionService, IRefreshTokenService refreshTokenService) { _sessionService = sessionService; _refreshTokenService = refreshTokenService; } [HttpPost("revoke-other-devices")] public async Task<IActionResult> RevokeOtherDevices(string userId) { // 撤销用户所有会话 await _sessionService.RevokeUserSessionsAsync(userId); // 撤销用户所有refresh_token await _refreshTokenService.RevokeRefreshTokensAsync(userId); return Ok("其他设备已被强制登出"); }
2. 确保客户端(MVC)的会话同步失效
除了IdentityServer的会话,你还需要在MVC客户端处理:
- 当用户触发强制登出操作时,调用Auth服务器的上述API。
- 可以考虑用分布式缓存(比如Redis)跟踪用户的有效客户端会话,当强制登出时,标记其他会话为无效,MVC客户端在每次请求时检查会话有效性,若无效则强制本地登出。
3. 可选:使用IdentityServer的前端/后端通知
如果你的客户端支持回调,IdentityServer可以发送会话失效通知,让客户端主动登出。你可以在IdentityServer的客户端配置中启用FrontChannelLogoutUri或BackChannelLogoutUri,当会话被撤销时,IdentityServer会调用这些端点通知客户端执行登出操作。
总结
删除refresh_token不足以实现强制登出其他设备,核心是要撤销用户在IdentityServer的所有登录会话,同时清理所有关联的refresh_token。这样其他设备的用户下次访问时,IdentityServer会要求重新认证,从而达到强制登出的效果。
内容的提问来源于stack exchange,提问作者jun ho

