OAuth2.0/OpenID 的Logout包括Authorization Server的Logout和Client的Logout。
Authorization Server的Logout
Authorization Server的Logout其实很简单,仅仅是简单的清除Login Cookie即可。可以调用Identity Server 4所提供的扩展方法SignOutAsync来实现。
Client的Logout
在Logout的过程中最复杂的是Client的Logout,因为Authorization Server与所有的Client之间其实构成了一个分布式系统(如下图)。Authorization Server Logout后需要通知所有Client Logout信息,然后所有Client相继Logout.
OpenID Connect协议中定义了三种方式来通知Client Logout。
Front-channel
这种方式的实现原理是每个Client在Authorization Server上注册一个用于Logout的页面Url(通过FrontChannelLogoutUri设置
)。当Authorization Server Logout后,在Logout页面上,将所有登录过的Client的Logout页面也iframe的形式嵌入。这样所有Client的Logout页面会被访问,最终所有Client的Login状态被清除。
这种方式主要适用于Server side client, 理论上SPA应用应该也可以实现这种方式。
这种方式最终是通过浏览器发送请求给Server端来Logout.所以称为Front-Channel.
Back-channel
相较于Front-Channel, Back-Channel是通过Authorization Server的后端直接调用Client的接口来清除某个用户的登录状态。这种方式没怎么使用过。目前还有一点没弄明白其Logout的原理。(比如MVC Client的认证状态是通过cookie管理,那这种方式下,MVC Client如何如清除某个用户的Cookie呢,毕竟Cookie是存在浏览器端的,而Back-Channel方式是Authorization Server与MVC Client之间的交互,和用户的浏览器无关。这里需要再研究一下。)
Session Management
SPA应用的Logout基本是靠轮询,即周期性的检测用户登录状态是否发生变化。如变化则触发重新认证。但是OpenID协议设计了一个巧妙的方法来避免周期性轮询Authorization Server造成网络资源浪费。如下图:
详细过程如下:
1. 在用户登录后,Authorization Server会向生成一个session id写入cookie idsrv.session.(改cookie的origin为authorization server所在域)
2. 在redirect url的重定向请求中,Authorization Server会在Url中添加一个名为session_state的Query string。该session_state的生成算法为Base64(SHA256(clientId+clientOrigin+sessionId+salt)),salt
Client会再redirect url所在页面中将session_state存入session storage(origin 为client所在域)
3. 在SPA页面中,需要加载两个iframe:client iframe(origin 为client所在域)和check session iframe(origin为authorization server 所在域)
4. 在client iframe中,会周期性的从session storage中取出session_state,然后通过PostMessage发送给check session iframe.
5. check session iframe收到来自client iframe的message后,会从authorization server域中取出cookie idsrv.session(sessinId), 然后用第2步中的算法重新生成session_state。
6. check session iframe通过比较第5步生成的session_state和client iframe发送过来的session_state可以知道SPA当前session是否发生变化。然后通过PostMessage将结果(changed或unchange)发送给client frame
7 如果第6步结果为changed, client iframe收到后会触发重新认证。
说明:第一步中的cookie: idsrv.session, 在用户Logout或者切换用户后会发生变化。因此可以在第6步中检测到。
基于Identity Server 4的实现
1. Front Channel Logout
只需在定义Client时为其设置FrontChannelLogoutUri,然后再Client端处理该请求是实现Client Logout的逻辑
2. Back Channel Logout
跟Front Channel Logout一样,只需在定义Client时为其设置BackChannelLogoutUri,然后再Client端处理该请求是实现Client Logout的逻辑
3. SPA Client Logout
oidc-client 提供实现。无需额外实现。