I have the following scenario:
I have workflow application object running a workflow activity. In the workflow activity logic, I call a WCF service in which the service operations is also generated as a set activities.
The problem is: How can I pass the credentials and impersonate other user in this case as its regularly done like this:
MyServiceClient client = new MyServiceClient();
client.ClientCredentials.Windows.ClientCredential.Domain = domain;
client.ClientCredentials.Windows.ClientCredential.UserName = username;
client.ClientCredentials.Windows.ClientCredential.Password = password;
I tried to impersonate the code block that runs the the workflow application object, but in that case, I get some kind of exception saying "Could not load file or assembly .... Access is denied". As I understand, the reason of this exception is that the workflow application is trying to call assembly which is under the original logged on user.
Any help please?
Windows authentication using the calling process's credentials is the best supported authentication method, but there are activities for handling username/password authentication in the WF Security Pack CTP 1 on the WF CodePlex site (https://wf.codeplex.com/releases/view/48114). See TokenFlowScope and GetUserNameSecurityToken. This article goes through it in detail: http://msdn.microsoft.com/en-us/magazine/gg598919.aspx. I haven't tried this, and would avoid it if at all possible.
For your second problem, there's a bit more to making workflows running under WorkflowApplication impersonate. From the same article:
The security context of a calling thread isn’t copied to the workflow thread, so even if the workflow client is impersonating, the WF thread—which is executing the activities—won’t be impersonating. The security context of the caller can be flown to the WF thread using a custom Synchronization context that would forward the call on the same incoming Async thread, similar to the Synchronization context used by the WorkflowInvoker
public class MySyncContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
d(state);
}
}
Related
I have a regular ASP.Net Core web site that users access using Windows Authentication to determine which users can access which pages.
In order to render a page for the user, the site needs to call in to a series of web services to fetch various bits of data. These web services don't use Windows Authentication. Instead, they require the user's JWT Token.
So, our WebSite needs to exchange the user's Windows token for a JWT token. We have a special ExchangeToken web service that accepts a request using Windows Authentication, and returns the user's JWT Token.
The difficulty comes when I want WebSite to call this ExchangeToken web service. I need to call it using Impersonation, so that I get the user's JWT Token back. However, it doesn't appear to be possible to use HttpClient with Impersonation.
Initially, I had planned to do this in WebSite:
Repeatedly...
Impersonate the user
Instantiate an HttpClient
Call the TokenExchange service to get the JWT Token
Dispose the HttpClient
Stop impersonation
Return the token
However, according to what I've read, re-creating an HTTP client for every call is bad practice, and I should be using HttpClientFactory instead.
However, I don't see how this approach can work with Impersonation.
I tried this:
Use HttpClientFactory to create an HttpClient
Repeatedly...
Impersonate the user
Call the TokenExchange service to get the JWT Token
Stop impersonation
Return the token
However, what happens is that, despite the impersonation, all calls to the TokenExchange service are made with the same windows credentials - the credentials of the user who happens to access the web site first. AFAIK, this stems from the way that Windows Authentication works - it performs a token exchange the first time you use an HttpClient, and from then on, all calls for that client use the same token.
One option would be to create a separate client for each user... but I have about 7,000 users, so that seems a bit excessive!
Another option would be to trust the WebSite to fetch the tokens on behalf of the user, using its own account. The problem with this is that it entails trusting the WebSite. If it is compromised by an attacker, then I can't stop the attacker stealing JWT tokens for arbitrary user. Whereas, with the impersonation, the attacker still can't get a user's JWT token without first obtaining their Windows token.
So, is there a way to do impersonation + IHttpClientFactory together? Or is there a better way to approach all this?
(If it matters, my company has its own Windows servers - we're not in the cloud, yet)
To demonstrate the problem with the second approach, I made a test application. It doesn't actually use HttpClientFactory, but it does demonstrate the problem.
I started with a web site that just returns the user who made a call:
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class WhoController : ControllerBase
{
[HttpGet]
public ActionResult<string> Get()
{
return User.Identity.Name;
}
}
My client code works like this:
private void CallClient(HttpClient httpClient, string username, string password)
{
LogonUser(username, "MYDOMAIN", password, 2, 0, out IntPtr token);
var accessTokenHandle = new SafeAccessTokenHandle(token);
WindowsIdentity.RunImpersonated(
accessTokenHandle,
() =>
{
string result = httpClient.GetStringAsync("http://MyServer/api/who").Result;
Console.WriteLine(result);
});
}
And my test code invokes it like this:
public void Test()
{
var httpClient = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true });
CallClient(httpClient, "User1", "Password1");
CallClient(httpClient, "User2", "Password2");
}
As described above, I get the following written to the console:
User1
User1
What I want is:
User1
User2
TL;DR: NET Core is doing a lot to fight you on this approach under the hood.
Not entirely an answer on what to do, but hopefully helpful background on the HttpClientFactory approach, based on my understanding of the components.
First, from the ASP NET Core docs in regards to impersonation:
ASP.NET Core doesn't implement impersonation. Apps run with the app's
identity for all requests, using app pool or process identity. If the
app should perform an action on behalf of a user, use
WindowsIdentity.RunImpersonated in a terminal inline middleware in
Startup.Configure. Run a single action in this context and then close
the context.
RunImpersonated doesn't support asynchronous operations and shouldn't
be used for complex scenarios. For example, wrapping entire requests
or middleware chains isn't supported or recommended.
As you call out, there's a lot of progress NET Core has made around how HttpClient instances are handled to resolve socket exhaustion and the expensive operations around the underlying handlers. First, there's HttpClientFactory, which in addition to supporting creating named/typed clients with their own pipelines, also attempts to manage and reuse a pool of primary handlers. Second, there's SocketsHttpHandler, which itself manages a connection pool and replaces the previous unmanaged handler by default and is actually used under the hood when you create a new HttpClientHandler. There's a really good post about this on Steve Gordon's Blog: HttpClient Connection Pooling in NET Core. As you're injecting instances of HttpClient around from the factory, it becomes way safer to treat them as scoped and dispose of them because the handlers are no longer your problem.
Unfortunately, all that pooling and async-friendly reuse makes your particular impersonation case difficult, because you actually need the opposite: synchronous calls that clean up after themselves and don't leave the connection open with the previous credentials. Additionally, what used to be a lower-level capability, HttpWebRequest now actually sits on top of HttpClient instead of the other way around, so you can't even skip it all that well by trying to run the requests as a one off. It might be a better option to look into using OpenID Connect and IdentityServer or something to centralize that identity management and Windows auth and pass around JWT everywhere instead.
If you really need to just "make it work", you might try at least adding some protections around the handler and its connection pooling when it comes to the instance that is getting used to make these requests; event if the new clients per request are working most of the time, deliberately cleaning up after them might be safer. Full disclaimer, I have not tested the below code, so consider it conceptual at best.
(Updated Switched the static/semaphore to a regular instance since the last attempt didn't work)
using (var handler = new SocketsHttpHandler() { Credentials = CredentialCache.DefaultCredentials, PooledConnectionLifetime = TimeSpan.Zero, MaxConnectionsPerServer = 1 })
using (var client = new HttpClient(handler, true))
{
return client.GetStringAsync(uri).Result;
}
I have a WCF which is configured on Windows Authentication. This means that only authenticated Windows users will be able to access. I have a method which does not return anything but spins up a thread that does some long running task under the called windows user impersonation.
My code is below:
public void SampleTask();
{
Task.Factory.StartNew(this.Activity);
}
private void Activity()
{
WindowsIdentity identity = ServiceSecurityContext.Current.WindowsIdentity;
using (WindowsImpersonationContext ctx = identity.Impersonate())
{
// Log status in a log database as "In Progress"
// Do long running task (accessing database as current user)
// retreive the results and save in a file
// Update the log database as "Complete"
}
}
My question is will the task still complete of retrieving the results and saving it and put the status as it should. Or will the impersonation will not work as there will be no open session. Or am I mistaken
Regards,
Girija Shankar
The session should remain open as long as the method is executing. Even if the method is returning void, the request that started the execution of the method will be replied to.
If you don't need a reply from the service, you can use the IsOneWay = true attribute on the method, which will tell the service to not send a reply to the client. Since this is a long-running method that is not returning data to the client, that makes it a good candidate for being a one-way method. In this case I'm not sure if the session would remain open or not, but it doesn't matter because the impersonation context will be scoped to the service, and have no dependency on the client.
In your method, you can see this because of the declaration:
WindowsIdentity identity = ServiceSecurityContext.Current.WindowsIdentity;
The variable identity is scoped to the Activity method.
using (WindowsImpersonationContect ctx = identity.Impersonate())
The variable ctx is likewise scoped to the using block within the Activity method.
The only time you would run into a problem that I can think of is if the service throws an exception and crashes - then of course the method wouldn't finish.
To sum up, since the impersonation is based on the identity the service is running under, and you're not returning any data to the client, session should have no impact on both the method running to completion or the identity the service is running under.
I have a WCF service with several operation.
Each operation has the 'PrincipalPermission' tag, something like this :
[PrincipalPermission(SecurityAction.Demand, Role = "Administrator")]
public ProductsDto GetAllProducts()
{
// Do operation here ...
}
Problem is - if the user is not part of the role 'Administratir' - the service throws the exception
Request for principal permission failed
This corrupts the client's channel to a 'Faulted' state.
I want to be able to catch this somehow and send the client a 'Fault' message,
so that the client knows he tried to do something he shouldn't, without faulting the channel !
I tried using a 'try-catch' block inside the operation, but it didn't help.
The exception occurs 'outside' the operation itself.
How can I solve this ?
There's a special interface IErrorHandler that you can implement and hook up with the WCF service to handle the exception. Using IErrorHandler will let you handle both the security exceptions and any exceptions thrown by the serialization code.
Another note on the client's channel getting into a faulted state: I'd recomment creating a new client object for each unit of work you perform from the client. Reusing the client object opens up for this kinds of problems.
I have below requirements:
(1) Perform 'action A' when user requests for it.
(2) We also want to perform the same 'Action A' twice in a day even if users don't request for it.
I have a WCF web service which has method XYZ that performs action A. The method XYZ will be called when user requests for it.
Now question is, can I schedule this action without creating window service (which can host this service) or creating proxy?
Is there a way to perform an action by user request and schedule that same action, using only one application?
No, WCF cannot be auto-scheduled. You need to implement a Scheduled Task (see Scheduling jobs on windows), a Windows Service with a timer (which you've said you don't want to do, if I understand correctly) or some other application with a timer.
You could start a thread as per the other answer but this relies on your service calling itself - I'd prefer to call it externally, from another process.
A scheduled task can run an executable. You could write a console application that calls your WCF service, logs any result (if necessary) and then completes.
I normally prefer to implement this type of timer through a Windows Service, simply because the Windows Service can be monitored, can log, and can auto-start / auto-restart - install it and it 'just works'. If I didn't want to use a Windows Service then I'd schedule a task.
I typically do this by just calling the WCF service method from some kind of task scheduler. In a really simple form, you could just spawn a Thread from your service, that runs the WCF method periodically. Again this isnt the best solution, but its easiest to demonstrate. You could use some other scheduler library to do this too...
[ServiceContract]
public class SomeClass
{
[ServiceOperation]
public void SomeServiceMethod() { ... }
Then somewhere in the application startup:
Thread t = new Thread(new ThreadStart(CallService));
t.Start();
...
// this will call the WCF service method once every hour
public void CallService()
{
Thread.Sleep(3600000); // sleep 1 hour
new SomeClass().SomeServiceMethod();
}
This is one way to do it, although not the best way, but basically you can just call the WCF service method just like any other method in the application.
It seems like I'm barking up the wrong tree when asking this question, this question and this question.
I need to authenticate users against a custom API (in COM), and I need to keep that custom API (the COM object) alive (for that user) for future WCF calls. During authentication against that custom API, I can get back a list of custom-defined roles. I'd also like to use these for authorization of the service methods.
Moreover, I need to be able to revoke the user's session remotely. This is triggered by an event raised by the COM API.
I've got a custom UserNamePasswordValidator, but it appears that this has no mechanism for correctly setting a custom principal, so it looks like I'm heading in the wrong direction.
How do I do these three things?
You can handle authentication completely in your service. Create service contract similar to:
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IService
{
// All your operations marked with [OperationContract(IsInitiating=false, IsTerminating=false)]
// Two additional operations
[OperationContract(IsInitiating=true, IsTerminating=false)]
void Login(string user, string password);
[OperationContract(IsInitiating=false, IsTerminating=true)]
void Logout();
}
Service implementing this contract has to have PerSession instancing. Implement authentication in Login method and store COM object in local field. When new client want to use such service he has to first call the Login method. So all your instances will be properly authenticated and they will store their instance of COM object.
You can also register InstanceContext and COM object to some global class which will deal with forcibly killing service instance. This will probably require some research to make it work.
Make sure that you use some secure binding (encryption) because you will send user name and password as a plain text.