Identity Server 4 User Impersonation
Asked Answered
I

0

7

I am struggling to implement a Impersonation feature into the Identity Server 4 Service. I understand that there's a lot of people who are against implementing it the way I want to but I really need the full redirect back to the SSO server in order to generate a new list of claims. The user that is being impersonated will have a completely different set of Rules associated with them as claims, so it must come from the IdSrvr.

I have read through https://github.com/IdentityServer/IdentityServer4/issues/853, and also IdentityServer4 - How to Implement Impersonation

Here's what I've attempted so far, We did this perfectly inside of Identity Server 3 with ACR values and the Pre-Auth even on the UserService.

This controller method I am calling from one of the Clients of my identity server:

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

Here is my OnRedirectToProvider:

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

    return Task.CompletedTask;
}

This is where i start to get lost, at the moment, I've inherited from the AuthorizeInteractionResponseGenerator class and implemented my own with the override to the ProcessLoginAsync (this is the only thing i could find that was close to the pre-auth event previously)

    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);
        }
    }

As you can see, i've tried a couple different things here, When not using OIDC as a authentication scheme, and my IdServer/Site is the same site, I had a function that impersonation worked with. Which is where _signInManager.ImpersonateAsync(...) is. Here is that Implementation:

    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;
    }

In an effort to simply 'replace' who was signing in, or at least who the identity server was thinking was signing in, i just replaced Request.Subject with the Impersonation Result. This doesn't actually change anything that I can find, at least not on my client app. If i use the redirect URI of 'https://localhost:44322/signin-oidc' (localhost because i'm running the sites locally), I get a "Correlation failed at signin-oidc redirect" message. If anyone has implemented something like this or done anything similar I would greatly appreciate the help getting this complete.

Suggestions welcome for completely different implementations, this was just my best stab at what worked flawlessly with idsrvr3.

Incessant answered 1/12, 2018 at 22:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.