首页 新闻 会员 周边

ASP.NET Core OpenID Connect 登出问题

0
悬赏园豆:30 [已解决问题] 解决于 2022-09-04 12:12

请问在 OP(OpenID Provider) 上登出(sign out)时如何让 RP(Relying Party) 应用也自动登出?

OP 基于 OpenIddict 实现,RP 基于 Microsoft.AspNetCore.Authentication.OpenIdConnect 实现

也就是站点A通过站点B进行单点登录(基于 OpenID Connect 实现),在站点B上退出登录时,站点A也同时退出登录,即单点登出 Single Logout (SLO)

dudu的主页 dudu | 高人七级 | 园豆:31007
提问于:2022-08-30 16:14
< >
分享
最佳答案
0

终于通过非标准的做法实现了。

(一)

服务端(基于 OpenIddict 实现的 OpenID Provider)在用户登出时 revoke 当前用户的所有 token

public bool RevokeToken(string subject)
{
    var result = _dbContext.Set<OpenIddictEntityFrameworkCoreToken>()
        .Where(t => t.Subject == subject && (t.Status == Statuses.Redeemed || t.Status == Statuses.Valid))
        .Update(t => new OpenIddictEntityFrameworkCoreToken { Status = Statuses.Revoked });

    return result > 0;
}

Sign-out endpoint 的实现

[HttpPost("/signout")]
public async Task<IActionResult> SignOut(string returnUrl)
{
    var userId = User.UCenter()?.UserId;
    if (userId.HasValue)
    {
        _openIddictService.RevokeToken(userId.ToString());
    }

    await HttpContext.SignOut();
    return Redirect(Url.SecuringUrl(returnUrl));
}

同时,服务端实现一个 web api 用于检查 toke id 对应的 token 是否失效

[HttpHead("validate-token-id")]
public async Task<IActionResult> ValidateTokenId()
{
    if (Request.Headers.TryGetValue("x-token-id", out var tokenId))
    {
        if (await _openIddictService.ValidateTokenId(tokenId))
        {
            return NoContent();
        }
    }

    return BadRequest();
}

ValidateTokenId 的实现

public async Task<bool> ValidateTokenId(string tokenId)
{
    var status = await _dbContext.Set<OpenIddictEntityFrameworkCoreToken>()
        .Where(t => t.Id == tokenId)
        .Select(t => t.Status)
        .FirstOrDefaultAsync();
    return status == Statuses.Redeemed || status == Statuses.Valid;
}

(二)

客户端(基于 ASP.NET Core 内置的 OpenIdConnect 实现的 Relying Party)实现一个 CookieAuthenticationEvents,在已登录的用户每次请求时检查 token id,如果 token 已经失效,自动登出当前用户。

public class CnblogsCookieAuthenticationEvents : CookieAuthenticationEvents
{
    public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
    {
        var identity = context.Principal.Identity;
        if (!identity.IsAuthenticated)
        {
            return;
        }

        var sp = context.HttpContext.RequestServices;
        var httpClientFactory = sp.GetRequiredService<IHttpClientFactory>();

        var tokenId = context.Principal.Claims.FirstOrDefault(c => c.Type == "oi_tkn_id")?.Value;

        if (!string.IsNullOrEmpty(tokenId))
        {
            using var client = httpClientFactory.CreateClient();
            client.BaseAddress = new Uri("https://account.cnblogs.com/");
            using var request = new HttpRequestMessage(HttpMethod.Head, "openiddict/validate-token-id");
            request.Headers.Add("x-token-id", tokenId);
            using var response = await client.SendAsync(request);

            if (response.StatusCode == HttpStatusCode.BadRequest)
            {
                context.RejectPrincipal();
                await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
                return;
            }
        }

        await base.ValidatePrincipal(context);
    }
}

在 AddCookie 时注册上面的实现

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.EventsType = typeof(CnblogsCookieAuthenticationEvents);
    });

(搞定)

dudu | 高人七级 |园豆:31007 | 2022-09-04 12:11
其他回答(1)
0

哈哈,这个问题太高深了,这么几天都没有人回答。
前段时间刚好也看了一下 OpenIddict ,看得晕乎乎的的,国内好像没人用过个,资料也少。

按我的理解:OpenID仅仅是在用的那一瞬间起一个凭证作用, 要实现单点登录还得各系统自己完成。

Adming | 园豆:119 (初学一级) | 2022-09-04 12:36


什么敏感词????

支持(0) 反对(0) Adming | 园豆:119 (初学一级) | 2022-09-04 12:45

支持(0) 反对(0) Adming | 园豆:119 (初学一级) | 2022-09-04 12:55

@Adming: OpenIddict 实现了 OpenID Connect 规范,单点登录是基于 OpenID Connect 规范,我们已经基于 OpenIddict 实现了。只是 OpenIddict 没有 IdentiyServer 做的那么全面,需要自己参考示例代码实现 connect/authorizeconnect/token endpoint。

支持(0) 反对(0) dudu | 园豆:31007 (高人七级) | 2022-09-04 14:47

目前 OpenIddict 不支持 Back-Channel Logout

支持(0) 反对(0) dudu | 园豆:31007 (高人七级) | 2022-09-04 14:48

@dudu: 不冲突,OpenID Connect 规范就相当于我上面的说“关于加强XX管理”的工作指导。
但具体怎么实现还是得B系统根据自己的需求选择实时检查、定期检查或不检查。IdentiyServer 完成度高,同步检查方面的工作都可以直接用现成的,OpenIddict 更简单灵活,可能需要自己写代码实现。

支持(0) 反对(0) Adming | 园豆:119 (初学一级) | 2022-09-04 15:02

@Adming: 重点工作就是 token 管理

支持(0) 反对(0) dudu | 园豆:31007 (高人七级) | 2022-09-04 16:57

@dudu: 是的,但token这个词太抽象了,抽象到它和“卡”,"证件"是一个概念了。

支持(0) 反对(0) Adming | 园豆:119 (初学一级) | 2022-09-04 17:58

支持(0) 反对(0) Adming | 园豆:119 (初学一级) | 2022-09-04 18:00
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册