How can I get my console app to connect with an IIS hosted WCF service when basic and/or windows authentication is switched on and anonymous authentication turned off?
The site is internal and stringent security is not required. There is no domain controller. However, I need to turn off anonymous access.
I have searched for days and have tried many methods including using a self hosted certificate and overriding the certification validation, overriding the UserNameValidator and using client.ClientCredentials.Windows.ClientCredentials.UserName or client.ClientCredentials.UserName.UserName. None of these have worked.
I am at a point where it would be nice if someone would be so kind as to look and run the code and help me get the sample running with authentication.
I have taken the liberty of uploading a sandbox solution containing, HostWebSite, ClientConsole and API projects.
I have hosted the zip file on my Windows Live SkyDrive: WCF_Authentication.zip
Some small setup steps.
I added to the hosts file 127.0.0.1 hostwebsite.local
I added a website to IIS
-- location: HostWebSite project root,
-- binding: hostwebsite.local
-- app pool: Classic 4.0 app pool.
Applied security Everyone read access to the HostWebSite project directory.
Verify can see service http://hostwebsite.local/services/EchoService.svc
Verify the console echoes back hello world.
Then turn off anonymous via IIS / Authentication and turn on either basic and/or windows authentication.
thank you
For the benefit of readers, I have included code snippets here
Project: API
namespace API.Contract
{
[ServiceContract]
public interface IEcho
{
[OperationContract]
string SendEcho(string message);
}
}
namespace API.Proxy
{
public class EchoProxy : IEcho
{
public string SendEcho(string message)
{
return string.Concat("You said: ", message);
}
}
}
namespace API.Service
{
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public class EchoService : System.ServiceModel.ClientBase<IEcho>, IEcho
{
public EchoService()
{
}
public EchoService(string endpointConfigurationName) :
base(endpointConfigurationName)
{
}
public EchoService(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public EchoService(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public EchoService(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}
public string SendEcho(string message)
{
return base.Channel.SendEcho(message);
}
}
}
Project: ClientConsole
static void Main(string[] args)
{
EchoService client = new EchoService("WSHttpBinding_IEcho");
try
{
Console.WriteLine(client.SendEcho("Hello World"));
client.Close(); // i tried putting this in the finally block but the client would close in an errored state it said.
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
Client Config
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IEcho" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://hostwebsite.local/Services/EchoService.svc/services/EchoService.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IEcho"
contract="API.Contract.IEcho" name="WSHttpBinding_IEcho">
<identity>
<servicePrincipalName value="host/mikev-ws" />
</identity>
</endpoint>
</client>
</system.serviceModel>
Project: HostWebSite
<system.serviceModel>
<!-- SERVER -->
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceTypeBehaviors">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="API.Proxy.EchoProxy" behaviorConfiguration="MyServiceTypeBehaviors">
<endpoint address="/services/EchoService.svc" binding="wsHttpBinding" contract="API.Contract.IEcho" />
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"/>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
Are you really looking at message level security? From your description, it appears that you want a transport level security (from IIS). For that you have to get your client configuration file correct. For example,
<binding ...
...
<security mode="TransportCredentialOnly">
<transport clientCredentialType="windows" proxyCredentialType="None" realm="" />
...
This will ensure integrated windows authentication - will use current windows user running the client for authentication. For NTLM/BASIC authentication, you need to provide user name/password from code - for example,
<binding ...
...
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" proxyCredentialType="None" realm="" />
And in code,
EchoService client = new EchoService("WSHttpBinding_IEcho");
client.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(userName, pwd);
EDIT:
For basic authentication to work with http protocol, you have to do configuration on server side as well as. For example,
<system.serviceModel>
<!-- SERVER -->
<bindings>
<basicHttpBinding>
<binding name="NewBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
...
<services>
<service name="API.Proxy.EchoProxy" ...
<endpoint binding="basicHttpBinding" bindingConfiguration="NewBinding" contract="API.Contract.IEcho" />
...
See this article for more info. BTW, you may want to consider HTTPS scheme because basic auth transmits password in plain text.
Related
I have added a ASMX service reference to my project by doing right click on the root --> add service reference.
I have it like this on my web.config file:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="xxx" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="serviceaddress"
binding="basicHttpBinding" bindingConfiguration="xxx"
contract="xxx" name="xxx" />
</client>
</system.serviceModel>
This service has a method that receives a string with a username and validates if it exists.
The problem is that I'm testing it on Postman and it's returning the following error message:
The content type text/html; charset=UTF-8 of the response message does not match the content type
I've already checked on other posts similar like this one but I'm not able to find the solution.
Here's the method that I invoke which is throwing the error:
public static List<UserInformation> GetUsersByUserName(string userName)
{
try
{
var usersServiceClient = new LDapServicesSoapClient();
var requestMessage = new LDapUserNameLookupRequest();
requestMessage.UserName = userName;
requestMessage.AccessKey = "secretkey";
var response = usersServiceClient.LDapGetUserByUserName(requestMessage);
return response.Users.ToList();
}
catch (CommunicationException e)
{
if (e.InnerException is QuotaExceededException)
{
throw new Exception("We have found many users, please write another filter");
}
else
{
throw new Exception(e.Message, e);
}
}
}
Adding this configuration to my web.config file did the magic:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="LDapServicesSoap" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" allowCookies="false" bypassProxyOnLocal="false" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text">
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="Certificate" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="address"
binding="basicHttpBinding" bindingConfiguration="LDapServicesSoap"
contract="LDapServices.LDapServicesSoap" name="LDapServicesSoap" />
</client>
</system.serviceModel>
I have a Restful service that has this OperationContract
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "json")]
Response JSONData(Request request);
and is configured in this way
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" closeTimeout="01:01:00"
openTimeout="01:01:00" receiveTimeout="01:10:00" sendTimeout="01:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
messageEncoding="Text" textEncoding="utf-8" transferMode="Streamed"
useDefaultWebProxy="true">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647"
maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default"/>
</security>
</binding>
</basicHttpBinding>
<webHttpBinding>
<binding name="WebHttpBinding_IService" closeTimeout="01:01:00" openTimeout="01:01:00"
receiveTimeout="01:10:00" sendTimeout="01:01:00" allowCookies="false"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
transferMode="Streamed" useDefaultWebProxy="true">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="None" />
</binding>
<binding name="webHttpBindingXML"/>
<binding name="webHttpBindingJSON"/>
</webHttpBinding>
</bindings>
<services>
<service name="SearchService.SearchService" behaviorConfiguration="ServiceBehaviour">
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address supplied above -->
<endpoint address ="" binding="webHttpBinding" bindingConfiguration="WebHttpBinding_IService" contract="SearchService.ISearchService" behaviorConfiguration="web">
<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
-->
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint
automaticFormatSelectionEnabled="true"
helpEnabled="true" />
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
The request object is formed in this way
[DataContract]
public class Query
{
[DataMember]
public string name { get; set; }
[DataMember]
public string birthDate { get; set; }
}
[DataContract]
public class Input
{
[DataMember]
public Query query { get; set; }
[DataMember]
public string login_username { get; set; }
[DataMember]
public string login_password { get; set; }
}
[DataContract]
public class Request
{
[DataMember]
public Input input { get; set; }
}
I created a small client to test this query and I've also tested it using WCFTestClient and works fine. If I try to access this through fiddler though I get a 415 Unsupported Media Type error.
I tried this in the following manner
POST, http://localhost:8080/HostDevServer/SearchService.svc/json, HTTP/1.1
User-Agent: Fiddler
Content-Type: application/json;charset=UTF-8
Host: localhost:8080
Content-Length: 368
{ "input": { "login_password": "pass", "login_username": "login name", "query": { "birthDate": "", "name": "robert" } } }
I also tried deploying this service and see if fiddler would be able to access it then, but things got even more confusing because now instead of getting a 415 I was getting a 400 complaining about 'Reference to an object not set to an instance of the object.' and the small command line client I created would complain about There was no endpoint listening at the location of my service.
My question is, is there something wrong with my configuration file, or did I compose the fiddler request in the wrong way? If yes how can I fix this so I can access my service normally?
I can see that you are using BodyStyle = WebMessageBodyStyle.Wrapped
This means that you need to wrap the actual data with the name of the variable expected by your OperationContract. In short this should work
{ "response": { "input": { "login_password": "pass", "login_username": "login name", "query": { "birthDate": "", "name": "robert" } } } }
If you want to keep the body content as is then I suggest you use Bare instead of Wrapped for the body style.
I converted my WCF Services today to use WSHttpBinding instead of BasicHttpBinding. It is still in the development stage and thus I am using self signed certificates. The example that I followed is located Here
After finally getting the code to work like the example illustrates (follow the examples of the configs in the code that one can download), I decided to proceed to use Channel Factories like I did before.
Now when I make a call to a WCF method, I can clearly see that the object that I am sending is populated with the expected values, but if I step into the WCF side, the values are their defaults. For example Guid's will be Empty Guid's and int's will be 0. Always.
Any idea what might be causing this? here is some of my code:
In the web.config:
<add key="ClientCertificate" value="WcfClient" />
<add key="ServerCertificate" value="WcfServer" />
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="CustomBehavior">
<clientCredentials>
<clientCertificate findValue="WcfClient"
x509FindType="FindBySubjectName"
storeLocation="CurrentUser"
storeName="My" />
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IDocumentsService" closeTimeout="00:10:00"
openTimeout="00:10:00" sendTimeout="00:10:00" maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647" messageEncoding="Mtom" allowCookies="true">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Certificate" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:58790/DocumentsService.svc"
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IDocumentsService"
contract="DocumentsService.IDocumentsService"
name="WSHttpBinding_IDocumentsService"
behaviorConfiguration="CustomBehavior">
<identity>
<dns value="WcfServer" />
</identity>
</endpoint>
</client>
</system.serviceModel>
This is my channel factory
public static class ServiceObjects
{
public static IDocumentsService DocumentsSVC { get { return GetDocServiceClient(); } }
#region Private Members
private static WSHttpBinding _DMBinding = new WSHttpBinding("WSHttpBinding_IDocumentsService");
private static EndpointIdentity _DMIdentity = EndpointIdentity.CreateDnsIdentity(ConfigurationManager.AppSettings.Get("ServerCertificate"));
private static EndpointAddress _DMEndpoint = new EndpointAddress(new Uri(ConfigurationManager.AppSettings.Get("DocumentsService")), _DMIdentity);
private static IDocumentsService GetDocServiceClient()
{
ChannelFactory<IDocumentsService> _docSvcFactory = new ChannelFactory<IDocumentsService>(_DMBinding, _DMEndpoint);
_docSvcFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
_docSvcFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, ConfigurationManager.AppSettings.Get("ClientCertificate"));
_docSvcFactory.Credentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, ConfigurationManager.AppSettings.Get("ServerCertificate"));
return _docSvcFactory.CreateChannel();
}
#endregion
}
When I call the service, on the Client side for example:
private static Guid _UserID = (HttpContext.Current.User as Titan.Web.Classes.Identity.CustomPrincipal).UserId;
ServiceObjects.DocumentsSVC.GetDocumentsByFolderID(new DocumentRequest { CurrentUserID = _UserID })
I can see _UserID is populated, but on the server side it's not.
This is in my service's config
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpointBinding" closeTimeout="00:10:00" openTimeout="00:10:00" sendTimeout="00:10:00" allowCookies="true" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Mtom">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security>
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="Titan.WCF.Documents.DocumentsService" behaviorConfiguration="DocumentsServiceBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" contract="Titan.WCF.Documents.IDocumentsService">
<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
-->
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="DocumentsServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<serviceCredentials>
<clientCertificate>
<!-- Remove the NoCheck in production, this is only for when we use a self signed cert -->
<authentication certificateValidationMode="PeerOrChainTrust" revocationMode="NoCheck" />
</clientCertificate>
<serviceCertificate findValue="WCfServer"
storeLocation="CurrentUser"
storeName="My"
x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Of course, since you've changed your configuration in the service you need to update the service reference so that the client too can update its configuration on its side. Unless you do that the client will keep calling the service with old configuration which it reads from its config file while the service runs with the new configuration settings.
It may be tiring, but it's the way it is.
It seems the issue lies in the way you make a call to the service using the following code:
ServiceObjects.DocumentsSVC.GetDocumentsByFolderID(new DocumentRequest { CurrentUserID = _UserID });
Here you are trying to invoke the service function without creating a proxy object of the service. And that is because you have written a static class in the service.
In a WCF service you can't use static classes. You need to create an instance of the class (service proxy object) and then invoke the service function.
I am not sure why this would make a difference, but it did.
I needed to update my service references.
A rookie mistake I guess, but why would that make a difference if the only thing I did was to change the bindings, endpoints etc?
I've got a WebService with ASP.NET sites and WCF services in the same web.config. Until now, I was able to use the ASP.NET impersionation in the WCF services by setting
<system.web>
<compilation targetFramework="4.0" debug="false"/>
<!-- switch custom errors of-->
<identity impersonate="true"/>
<customErrors mode="Off"/>
</system.web>
However, now (for other reasons-> Cookieless Session state for the ASP.NET part) I have to set the
aspNetCompatibilityEnabled="true"
option to false. With this I loose the ASP.NET impersionation for the WCF services.
One of my WCF services needs impersionation for IO operations on the server...
I would like to know how to get the same impersionation I had before by directly defining it on the WCF service configuration.
What I have tried (unsucessfully) is to set
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
on the implementation of the methods in the WCF service and then specifying
<endpoint address="" binding="wsHttpBinding" contract="IService">
<identity>
<servicePrincipalName value="HOST/YourMachineName" />
<dns value="" />
</identity>
</endpoint>
in the web.config (obviously with the correct values for my service), as described in http://msdn.microsoft.com/en-us/library/ff650591.aspx.
However, the WCF service can not be called anymore after this... It tells me that the WsHttpBinding does not offer an identity for the contract.
Am I missing something important?
Edit: Translation of the error message:
: The contract operation '{0}' requires Windows identity for automatic impersonation. A Windows identity that represents the caller is not provided by binding ('{1}','{2}') for contract ('{3}','{4}'.
(The original error message was german...)
Try adding someting similar to this
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="DelegationBehaviour">
<clientCredentials>
<windows allowNtlm="false" allowedImpersonationLevel="Delegation"></windows>
</clientCredentials>
<dataContractSerializer maxItemsInObjectGraph="4194304"></dataContractSerializer>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_SampleWebService" >
<readerQuotas maxArrayLength="16384" maxBytesPerRead="4096" maxDepth="32" maxNameTableCharCount="16384" maxStringContentLength="8192"></readerQuotas>
<security mode="TransportCredentialOnly">
<message algorithmSuite="Default" clientCredentialType="UserName"></message>
<transport clientCredentialType="Windows" proxyCredentialType="None" realm=""></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://server/WebServices/Service/Service.svc" behaviorConfiguration="DelegationBehaviour" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_SampleWebService" contract="SampleWS" name="BasicHttpBinding_SampleEndpoint"></endpoint>
</client>
</system.serviceModel>
This is the server side code
<system.serviceModel>
<services>
<service behaviorConfiguration="CustomBehavior" name="CustomWebService">
<endpoint address="" behaviorConfiguration="" binding="basicHttpBinding" bindingConfiguration="basicHttpBinding_Service" contract="WebService"/>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="basicHttpBinding_Service" maxReceivedMessageSize="4194304" receiveTimeout="00:30:00">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="CustomBehavior">
<dataContractSerializer maxItemsInObjectGraph="4194304" ignoreExtensionDataObject="True"/>
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true"/>
<serviceAuthorization impersonateCallerForAllOperations="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
As well as having these over our WebMethods
<WebMethod(), OperationContract(), OperationBehavior(Impersonation:=ImpersonationOption.Required)> _
Works for us
Well, in the end I just made the binding use Windows authentication:
<security mode="TransportWithMessageCredential">
<message negotiateServiceCredential="false" clientCredentialType="Windows" algorithmSuite="Default"/>
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
</security>
and passed a specific Windows user/pwd combination in the client:
channelFactory.Credentials.Windows.ClientCredential = new NetworkCredential(#"", "", "");
channelFactory.Credentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
Additionally I had to specifically use the newly impersonated user in the code of the web service:
using (var imp = ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
{
// do IO here
}
Well, the actual (underlying) question still remains:
How is it possible to emulate the ASP.NET functionality correctly...
For the moment I'm ok with the solution, however I've got the feeling that I've missed an important point about the ASP.NET impersonation.
Thanks a lot to Iain, although it wasn't exactly the correct answer, it at least got me on the right track!
I am learning WCF and right now I am getting one exception while running the application:
Exception:
Could not find default endpoint
element that references contract
'IService1' in the ServiceModel client
configuration section. This might be
because no configuration file was
found for your application, or because
no endpoint element matching this
contract could be found in the client
element.
Service Code:
namespace StockService
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
public string GetCompositedata()
{
CompositeType ct = new CompositeType();
return ct.StringValue;
}
}
}
web.config:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
now using svcutil I have generated proxyclass(generatedProxy.cs) and config file (serviceapp.config) and added it to a console application(client)
Client:
Service1Client sc = new Service1Client();
Console.WriteLine(sc.GetCompositedata());
Console.ReadKey();
config:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:2614/Service1.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService1" contract="IService1"
name="BasicHttpBinding_IService1" />
</client>
</system.serviceModel>
</configuration>
I am not able to figure out why I am getting this exception.
Please help
Is that config you display for the client part of the client app's app.config or web.config, if it's a web site / web application??
You need to include those parts in your application's config - it's not enough to have those config's in a separate file created by svcutil.exe - it needs to be part of your application's configuration.
The svcutil-generated configuration needs to go into your main app.config, not be included as-is.
You need to add an app.config to your project then merge the contents of the svcutil config into the configuration section.