IdentityServer4源码解析_6_结束会话接口

目录

协议简析

会话管理属于可选协议内容,地址如下

https://openid.net/specs/openid-connect-session-1_0.html

认证服务元数据

以下参数必须在元数据中提供

登出的两种方式

前端登出

客户端在向认证服务注册的时候需要提供frontchannel_logout_uri(前端登出地址)。域名端口和架构必须与redirect_uri一致。

登出地址必须是绝对地址,可以包括application/x-www-form-urlencoded编码的query参数。

认证服务在页面中渲染一个隐藏的iframe,src指向frontchannel_logout_uri

<iframe src="frontchannel_logout_uri">

客户端的frontchannel_logout_session_required属性,决定认证服务向客户端发送登出请求的时候是否带上isssid参数。

前端登出 - 认证服务发起

如果有多个客户端登入,认证站点会有多个iframe,登出的时候逐个通知。

认证服务元数据中的frontchannel_logout_supported说明是否支持前端登出。frontchannel_logout_session_supported说明登出是否支持传递isssid参数。

sid : session id,会话id。

示例:
客户端注册frontchannel_logout_uri为https://rp.example.org/frontchannel_logout,frontchannel_logout_session_required为true,认证服务渲染如下html代码段触发前端登出。

<iframe src="https://rp.example.org/frontchannel_logout
    ?iss=https://server.example.com
    &sid=08a5019c-17e1-4977-8f42-65a12843ea02">
</iframe>

前端登出 - 客户端发起

客户端注册的时候提供post_logout_redirect_uris,前端登出后跳转到此地址,此地址只有在客户端发起的登出才会跳转。

详细内容查看协议

后端登出

认证服务发送logout_token到客户端,参数有:

示例:

{
"iss": "https://server.example.com",
"sub": "248289761001",
"aud": "s6BhdRkqt3",
"iat": 1471566154,
"jti": "bWJq",
"sid": "08a5019c-17e1-4977-8f42-65a12843ea02",
"events": {
    "http://schemas.openid.net/event/backchannel-logout": {}
    }
}

后端登出 - 认证服务发起

认证服务向客户端发起POST请求,参数用application/x-www-form-urlencoded编码

  POST /backchannel_logout HTTP/1.1
  Host: rp.example.org
  Content-Type: application/x-www-form-urlencoded

  logout_token=eyJhbGci ... .eyJpc3Mi ... .T3BlbklE ...

客户端收到登出请求后,定位到要登出的会话,注销当前会话不应当撤销已颁发的refresh_token

详细内容查看协议

后端登出 - 客户端发起

客户端在本地登出后,向end_session_endpoint接口发起请求,通知认证中心退出。
请求需包含下列参数:

请求logout接口,认证服务需要询问用户是否要登出认证中心。如果用户确认退出,认证服务必须登出当前用户。

源码简析

public async Task<IEndpointResult> ProcessAsync(HttpContext context)
    {
        if (!HttpMethods.IsGet(context.Request.Method))
        {
            _logger.LogWarning("Invalid HTTP method for end session callback endpoint.");
            return new StatusCodeResult(HttpStatusCode.MethodNotAllowed);
        }

        _logger.LogDebug("Processing signout callback request");

        var parameters = context.Request.Query.AsNameValueCollection();
        var result = await _endSessionRequestValidator.ValidateCallbackAsync(parameters);

        if (result.IsError == false)
        {
            _logger.LogInformation("Successful signout callback.");

            if (result.FrontChannelLogoutUrls?.Any() == true)
            {
                _logger.LogDebug("Client front-channel iframe urls: {urls}", result.FrontChannelLogoutUrls);
            }
            else
            {
                _logger.LogDebug("No client front-channel iframe urls");
            }

            if (result.BackChannelLogouts?.Any() == true)
            {

                _logger.LogDebug("Client back-channel iframe urls: {urls}", result.BackChannelLogouts.Select(x=>x.LogoutUri));
            }
            else
            {
                _logger.LogDebug("No client back-channel iframe urls");
            }

            await InvokeBackChannelClientsAsync(result);
        }

        return new EndSessionCallbackResult(result);
    }