Impersonate a Client’s Identity in Wcf
Posted by Dan Rigsby on April 17th, 2008
Impersonation is the process of impersonating a user in an application that isn’t necessarily running as that user. In Wcf, a service may need to impersonate the user of the client in order to access a resource such as a file share, another machine, etc. The service may even need to call a different Wcf service on the client’s behalf.
First off, you need to be using a binding that supports impersonation. They are:
- WSHttpBinding, WSDualHttpBinding, and NetTcpBinding with a Windows client credential.
- BasicHttpBinding with a BasicHttpSecurityMode set to the TransportWithMessageCredential credential, or any other standard binding where the client presents a user name credential that the service can map to a valid Windows account.
- Any CustomBinding that uses a Windows client credential with the requireCancellation set to true. (The property is available on the following classes: SecureConversationSecurityTokenParameters, SslSecurityTokenParameters, and SspiSecurityTokenParameters.) If a secure conversation is used on the binding, it must also have the requireCancellation property set to true.
- Any CustomBinding where the client presents a user name credential. If secure conversation is used on the binding, it must also have the requireCancellation property set to true.
To setup impersonation you can follow one of two models: Declarative and Imperative.
Declarative Model
- NotAllowed: Impersonation is not performed. If ImpersonateCallerForAllOperations is equal to true, a validation exception occurs at service startup time.
- Allowed: Impersonation is performed if credentials are available and ImpersonateCallerForAllOperations is equal to true.
- Required: Impersonation is required. This method can’t run unless the user can be impersonated. The value of ImpersonateCallerForAllOperations doesn’t matter here. Impersonation always takes place.
The default value is NotAllowed. So if you are going to use ImpersonateCallerForAllOperations, you will need to make sure to set all of your methods to ImpersonationOption.Allowed. If you are not using ImpersonateCallerForAllOperations and just want to use a impersonation for a single method, the set ImpersonationOption.Required.
[OperationBehavior(Impersonation=ImpersonationOption.Required)] public bool MyMethod(string input) { return true; }
Imperative model
If you chose to setup the impersonation in code, System.ServiceModel.ServiceSerurityContext has everything you need. But, if the declarative model is so easy to use, why would you want to set this up through code? Well, this model is best used if only a part of your method needs to use impersonation. The declarative model uses client impersonation for everything in the method. Depending on what your system is doing, one model may be better than another.
public bool MyMethod(string myParameter) { // Set security context for the current user System.ServiceModel.ServiceSecurityContext securityContext = System.ServiceModel.ServiceSecurityContext.Current; // Make sure there is a windows identity present and impersonate it if (securityContext != null && securityContext.WindowsIdentity != null) { // Can check the securityContext.WindowsIdentity.ImpersonationLevel // here to make sure we are allowed to impersonate before // actually creating the impersonation context using (securityContext.WindowsIdentity.Impersonate()) { // TODO: Perform operations here as the impersonated user } } return false; }
Impersonation for all Methods
If you need to use impersonation for all methods on a service. You can use the ServiceAuthorizationBehavior and set the ImpersonateCallerForAllOperations property to true. This can be done programmatically on the servicehost like so:
ServiceAuthorizationBehavior serviceAuthoriationBehavior =
serviceHost.Description.Behaviors.Find<ServiceAuthorizationBehavior>();
serviceAuthoriationBehavior.ImpersonateCallerForAllOperations = true;
Or through the application configuration as such:
<behaviors>
<serviceBehaviors>
<behavior name="myBehavior">
<serviceAuthorization impersonateCallerForAllOperations="true" />
</behavior>
</serviceBehaviors>
</behaviors>
Client Configuration
- None: An impersonation level is not assigned.
- Anonymous: The server process cannot obtain identification information about the client, and it cannot impersonate the client.
- Identification: The server process can obtain information about the client, such as security identifiers and privileges, but it cannot impersonate the client. This is useful for servers that export their own objects, for example, database products that export tables and views. Using the retrieved client-security information, the server can make access-validation decisions without being able to use other services that are using the client’s security context.
- Impersonation: The server process can impersonate the client’s security context on its local system. The server cannot impersonate the client on remote systems.
- Delegation: The server process can impersonate the client’s security context on remote systems.
If you need impersonation, you will have to choose between Impersonation or Delegation depending on your needs.
If you are using a proxy client to connect to the service, you could setup the impersonation level like so:
// Create a new client and set the impersonation level MyServiceClient client = new MyServiceClient(); client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
// Create a new channel factory and set the impersonation level ChannelFactory<IMyService> channelFactory = new ChannelFactory<IMyService>(); channelFactory.Credentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
Impersonate a totally different Windows User in a client
There may also be times when on the client you want to impersonate a different user than what is currently being used. To do this, you must know the domain, username, and password for that user. You then just use these values to set the client credential for the client:
MyServiceClient client = new MyServiceClient();
client.ClientCredentials.Windows.ClientCredential.Domain = domain;
client.ClientCredentials.Windows.ClientCredential.UserName = username;
client.ClientCredentials.Windows.ClientCredential.Password = password;
client.ClientCredentials.Windows.AllowedImpersonationLevel =
System.Security.Principal.TokenImpersonationLevel.Impersonation;
Or through a ChannelFactory as such:
ChannelFactory<IMyService> channelFactory =
new ChannelFactory<IMyService>();
channelFactory.Credentials.Windows.ClientCredential.Domain = domain;
channelFactory.Credentials.Windows.ClientCredential.UserName = username;
channelFactory.Credentials.Windows.ClientCredential.Password = password;
channelFactory.Credentials.Windows.AllowedImpersonationLevel =
System.Security.Principal.TokenImpersonationLevel.Impersonation;

















April 19th, 2008 at 8:16 pm
[...] Impersonate a Client’s Identity in WCF (Dan Rigsby) [...]
June 26th, 2008 at 7:55 pm
[...] http://www.danrigsby.com/blog/index.php/2008/04/17/impersonate-a-clients-identity-in-wcf/ [...]
July 25th, 2008 at 1:14 am
How do I handle Invalid credential?
In svctraceviewer, the client kept on trying in never ending loop.
System.ServiceModel.Security.SecurityNegotiationException exception was thrown from WCF service “The server has rejected the client credentials.” without sending reply back from service to client until client times out.
July 25th, 2008 at 5:20 am
You could create a FaultException to return to the client.
December 24th, 2008 at 8:32 am
dear Dan,
excellent article, just what I was looking for.
Do you happen to know if NetTcpContextBinding support impersonation?
March 5th, 2010 at 11:29 am
In Silverlight, the (Client)Credentials do not seem to have a Windows property, and System.Security.Principal.TokenImpersonationLevel does not seem to exist either. What to do?