Identity Server 4 用户模拟

bre*_*vid 7 impersonation asp.net-identity asp.net-core identityserver4

我正在努力在 Identity Server 4 服务中实现模拟功能。我知道有很多人反对以我想要的方式实现它,但我确实需要完全重定向回 SSO 服务器才能生成新的声明列表。被模拟的用户将拥有一组完全不同的规则作为声明与他们关联,因此它必须来自 IdSrvr。

我已经通读了https://github.com/IdentityServer/IdentityServer4/issues/853以及IdentityServer4 - 如何实现模拟

这是我到目前为止所尝试的,我们在 Identity Server 3 中使用 ACR 值和 Pre-Auth 完美地做到了这一点,甚至在 UserService 上也是如此。

我从身份服务器的客户端之一调用此控制器方法:

public IActionResult Impersonate(string userIdToImpersonate, string redirectUri)
{
        return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties(){RedirectUri = redirectUri, Items = { {"acr_values", $"userIdToImpersonate:{userIdToImpersonate}"}}});            
}
Run Code Online (Sandbox Code Playgroud)

这是我的 OnRedirectToProvider:

OnRedirectToIdentityProvider = context =>
{
    if (context.Properties.Items.ContainsKey("acr_values"))
    {
       context.ProtocolMessage.AcrValues = context.Properties.Items["acr_values"].ToString();
    }

    return Task.CompletedTask;
}
Run Code Online (Sandbox Code Playgroud)

这是我开始迷路的地方,目前,我已经从AuthorizeInteractionResponseGenerator类继承并实现了我自己的覆盖 ProcessLoginAsync (这是我能找到的唯一接近 pre-auth 事件之前的事件)

    protected override async Task<InteractionResponse> ProcessLoginAsync(ValidatedAuthorizeRequest request)
    {
        if (!request.IsOpenIdRequest) return await base.ProcessLoginAsync(request);
        var items = request.GetAcrValues();
        if (items.Any(i => i.Contains("userIdToImpersonate")) && request.Subject.IsAuthenticated())
        {
            //handle impersonation
            var userIdToImpersonate = items.FirstOrDefault(m => m.Contains("userIdToImpersonate")).Split(':').LastOrDefault();
            request.Subject = await _signInManager.ImpersonateAsync(userIdToImpersonate);




            //var userToImpersonate = await _signInManager.UserManager.FindByIdAsync(userIdToImpersonate);
            //if (userToImpersonate == null) return await base.ProcessLoginAsync(request);

            //var userBeingImpersonated = await _signInManager.UserManager.FindByIdAsync(userIdToImpersonate);

            //var currentUserIdentity = await _signInManager.CreateUserPrincipalAsync(userBeingImpersonated);
            //var currentClaims = currentUserIdentity.Claims.ToList();
            //currentClaims.Add(new Claim(IdentityServiceClaimTypes.ImpersonatedById, request.Subject.IsBeingImpersonated() ? request.Subject.GetClaimValue(IdentityServiceClaimTypes.ImpersonatedById) : _signInManager.UserManager.GetUserId(request.Subject)));
            //request.Subject = new ClaimsPrincipal(new ClaimsIdentity(currentClaims));

            //return await base.ProcessLoginAsync(request);



            return new InteractionResponse();
        }
        else
        {
            return await base.ProcessLoginAsync(request);
        }
    }
Run Code Online (Sandbox Code Playgroud)

如您所见,我在这里尝试了几种不同的方法,当不使用 OIDC 作为身份验证方案,并且我的 IdServer/Site 是同一个站点时,我有一个可以使用模拟的功能。这就是 _signInManager.ImpersonateAsync(...) 所在的位置。这是实现:

    public async Task<ClaimsPrincipal> ImpersonateAsync(string userIdToImpersonate)
    {
        var userBeingImpersonated = await UserManager.FindByIdAsync(userIdToImpersonate);
        if (userBeingImpersonated == null) return null;

        var currentUserIdentity = await CreateUserPrincipalAsync(userBeingImpersonated);
        var currentClaims = currentUserIdentity.Claims.ToList();
        currentClaims.Add(new Claim(IdentityServiceClaimTypes.ImpersonatedById, Context.User.IsBeingImpersonated() ? Context.User.GetClaimValue(IdentityServiceClaimTypes.ImpersonatedById) : UserManager.GetUserId(Context.User)));
        //sign out current user
        await SignOutAsync();

        //sign in new one
        var newIdentity = new ClaimsPrincipal(new ClaimsIdentity(currentClaims));
        await Context.SignInAsync(IdentityConstants.ApplicationScheme, newIdentity);
        return Context.User;
    }
Run Code Online (Sandbox Code Playgroud)

为了简单地“替换”正在登录的人,或者至少身份服务器认为正在登录的人,我只是用模拟结果替换了 Request.Subject。这实际上并没有改变我能找到的任何东西,至少在我的客户端应用程序上没有。如果我使用“ https://localhost:44322/signin-oidc ”的重定向 URI (本地主机,因为我在本地运行站点),我会收到“在 signin-oidc 重定向时关联失败”消息。如果有人已经实现了这样的事情或做过类似的事情,我将非常感谢帮助完成这项工作。

欢迎对完全不同的实现提出建议,这只是我对与 idsrvr3 完美配合的最佳尝试。