I have a WCF Service running on a Server, which is configured to accept Kerberos authentication.
Kerberos works fine and the WCF Service therefore knows, which user is connecting to him. The Service offers everything as Async Methods. Like this here (just an example for clearity).
public ExampleService : IExampleService {
public Task<string> GetUsernameAsync() {
return await Task.Run(() => System.Threading.Thread.CurrentPrincipal.Name);
}
}
On the Client side I have an Controller (it's an MVC-page, but that does not matter), which calls the methods asyncronously.
public ExampleController {
public async Task<ActionResult> Index() {
using(var serviceClient = ServiceFactory.GetServiceClient())
using(Security.Impersonation.Impersonate())
{
var data = await serviceClient.GetUsernameAsync();
return View(data);
}
}
}
The impersonation works fine, as long as I do not use await.
Since Task<>
does not flow the impersonated identity, I'd like to know if there is some possibility, to change the executing user of the Task
or to do anything else to make the impersonation work in this use-case.
I tried a custom awaiter (as it can be done with Culture in that Case), but that does not work at all (Well it just does not impersonate as well).
Since I am in charge of the WCF interfaces here is one solution, which works (but which I do not like, since it is more or less code duplication):
I have to say this is a workaround - not a solution - and it changes the Interfaces on Server-Side (to non-Async interfaces only), but at least it is working.
One plus for this solution - you can implement the impersonation as a behavior pattern on top of the ExampleServiceClient.
Okay - after some more in depth research I finally found the solution how to flow impersonated windows identities across asynchronous tasks.
The solution is machine-wide and will be set for all (in this case) 64bit ASP.NET 4.5 applications.
Find the
aspnet.config
file inC:\Windows\Microsoft.Net\Framework64\v4.0.30319
(probably this will apply for later versions, too) and change the value oflegacyImpersonationPolicy
to falseMake sure to restart IIS (or reboot the machine).
This will then make Impersonation flowing, as long as you use managed methods for the impersonation. In my case I impersonate similar to this, which works fine:
The aspnet.config setting (btw. it did not work to set it in the web.config file) is explained here: http://msdn.microsoft.com/en-us/library/ms229296(v=vs.110).aspx (it basically says, if this is true, we do it the .NET 1.1 way)
You can check, if the windows identity is flowed or not by using this method:
I disagree with your QUESTION.
The problem isn't your
await
. But yourTask.Run
. There should really not be aawait Task.Run
on you ASP.Net code. The effect of it is an unnecessary thread switch. Since you don't have STA threads on ASP.Net, there is no need for this and it just slows down your code.If you stick to real threadless
Task
s you shouldn't have any problems, as you will stay in a single thread. Unless your application server has a very limited number of clients and a huge amount of CPU bound operations, multi-threading is bad for scaling, as a single user can quickly fill up the schedule of your server.You should really be using
Task.FromResult
, orTaskCompletionSource.Task
to ensure that you remain single-thread. Which co-incidentally will fix your problem with[ThreadLocal]
properties.TL:DR
Don't use
Task.Run
on your server-side. UseTask.FromResult
so you only have one thread.EDIT:Response
Which thread? On the client side you are still going to use
await
. I never said don't useawait
. I said DON'T ever useawait
directly withTask.Run
(except on the UI thread). I didn't say you should BLOCK a thread. As your thread should be doing WORK to produce the result that you pass into theTask.FromResult
. BLOCKING means that you thread does nothing, whilst consuming resources (namely memory). Heck, there isn't even a need toServer side should use this pattern:
client should remain
and in the case where your ServiceClient resolves locally, everything runs synchronously (faster and with less resources). The point here is that you are only applying the
Task
async
pattern for the There is no thread style of async.Task.Run
is the concurrency style of async, and should only be used when you need to use another thread (either because you are CPU bound, or THIS thread NEEDS to be used for something else).