Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.Net Core WindowsIdentity impersonation does not seem to be working

I have the following code:

var baseUrl = "https://" + GetIdentityProviderHost(environment) + "/oauth2/authorize";
var query = $"?scope=openid&response_type=code&redirect_uri={redirectUrl}&client_id={clientId}";
var combinedUrl = baseUrl + query;

var currentUser = WindowsIdentity.GetCurrent(); 

await WindowsIdentity.RunImpersonated(currentUser.AccessToken, async() =>
{
    using (var client = new WebClient{ UseDefaultCredentials = true })
    {
        var response = client.DownloadString(combinedUrl);          
        Console.WriteLine(response);
    }
});

It basically constructs a URL and then calls it.

The call returns with a 401 (Unauthorized).

But if I take the combinedUrl and paste it into chrome or postman it works perfectly. That tells me that my call can work because Chrome is using my Windows Credentials to make the call.

I added the WindowsIdentity.RunImpersonated code to try to get around this issue. But it seems to not have had any effect.

How can I make a web call using Integrated Windows Authentication (IWA)?


Details:

If I run the following cURL command it works:

curl -L --negotiate -u : -b ~/cookiejar.txt "https://myIdp.domain.net/oauth2/authorize?scope=openid&response_type=code&redirect_uri=https://localhost:5001&client_id=my_client_id_here"

I am not sure how to replicate all that in C# code.

FYI: I have asked about this cURL command specifically in this question (since this question was focused on impersonation): Replicate cURL Command Using Redirect and Cookies in .Net Core 3.1

like image 864
Vaccano Avatar asked Mar 24 '20 21:03

Vaccano


3 Answers

I don't have a Windows box in front of me, so I cannot verify this thoroughly. But this seems to be a bit of a snake pit, based on the discussion e.g. here (especially from this comment down): https://github.com/dotnet/runtime/issues/24009#issuecomment-544511572

There seems to be various opinions on how to keep the Identity across an async call.

but if you look at the example in that comment,

app.Use(async (context, next) =>
{
    await WindowsIdentity.RunImpersonated(someToken, () => next());
});

it doesn't look like the func you send in as the second argument of WindowsIdentity.RunImpersonated should be async.

Have you tried:

var baseUrl = "https://" + GetIdentityProviderHost(environment) + "/oauth2/authorize";
var query = $"?scope=openid&response_type=code&redirect_uri={redirectUrl}&client_id={clientId}";
var combinedUrl = baseUrl + query;

var currentUser = WindowsIdentity.GetCurrent(); 

await WindowsIdentity.RunImpersonated(currentUser.AccessToken, () =>
{
    using (var client = new WebClient{ UseDefaultCredentials = true })
    {
        var response = client.DownloadString(combinedUrl);          
        Console.WriteLine(response);

    }
});

You can find the Microsoft docs on WindowsIdentity.RunImpersonated here: https://docs.microsoft.com/en-us/dotnet/api/system.security.principal.windowsidentity.runimpersonated?view=netcore-3.1

like image 138
Erik A. Brandstadmoen Avatar answered Sep 23 '22 11:09

Erik A. Brandstadmoen


.Net 5 running Impersonated (I am running this in Blazor)

I have spent a lot of time solving this and so I’m sharing my findings and my solution to hopefully help others avoid the pain!

Findings: On IIS the following code gets the IIS Appool account that the site is running under,

var currentUser = WindowsIdentity.GetCurrent();

So when you use the AccessToken, you are using the token for the wrong account.

I have also seen a lot of references to using an IHttpConextAccessor and lots of problems with this being null. This article from Microsoft suggests that this shouldn’t be used (Certainly in Blazor) MS-Docs

Solution: To get the user to impersonate use the AuthenticationStateProvider and get the user from this and cast to a WindowsIDentity to retrieve the AccessToken. This works in both a controller and a razor component. Inject the AuthenticationStateProvider and then in your method use the following code:

    var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
    var user = authState.User;
    var userToImpersonate = (WindowsIdentity)user.Identity;
    
    await WindowsIdentity.RunImpersonatedAsync(userToImpersonate.AccessToken, async () => 
  {
    
      // Your Code in here
    
  }

The windows Impersonation is only for Windows so if you want to suppress the Visual studio warnings surround the code with the following:

#pragma warning disable CA1416 // Validate platform compatibility
...
#pragma warning restore CA1416 // Validate platform compatibility
like image 36
Ren Avatar answered Sep 23 '22 11:09

Ren


Sadly, I wasn't able to reproduce your problem, impersonation is working fine for me when using this code:

WindowsIdentity identity = WindowsIdentity.GetCurrent();

using (identity.Impersonate())
{
    HttpWebRequest request = (HttpWebRequest) WebRequest.Create("https://my-address");
    request.UseDefaultCredentials = true;

    HttpWebResponse response = (HttpWebResponse) request.GetResponse();
}

I tested this with .NET Framework only, but since you already tried to setup Credentials manually, I guess it's not the .NET Core impersonation problem that was mentioned in one of the comments.

So my guess is that the problem is related to the address you are trying to access.

What might be the problem is the redirection, which I was not able to test, but you may want to try solution from this answer. You would use request.AllowAutoRedirect = false since the default value is true and in that case authorization header is cleared on auto-redirects (MSDN AllowAutoRedirect Property).

Other than that, you might also want to try using request.ImpersonationLevel = TokenImpersonationLevel.Delegation (MSDN ImpersonationLevel Property) or request.PreAuthenticate = true (MSDN PreAuthenticate Property).

As I said, I was not able to reproduce problem, so these are just some ideas which might (or might not) work for you...

like image 36
Nemanja Banda Avatar answered Sep 26 '22 11:09

Nemanja Banda