Share ASP.NET Session Id between WCF and a Silverlight Http client - asp.net

I have a WCF service configured to use ASP.NET session state. I have tested this WCF service with a WPF client and the session state is maintained across the different web requests.
Now I am trying to use this same WCF service from a Silverlight app which uses the new Http stack independent from the browser. I need to use this stack in order to be able to understand our WCF service faults. My problem is that in this case we are not able to read from the responses the Set-Cookie header with the ASP.NET_SessionId cookie or set the Cookie header in the requests.
This is the binding from the Silverligth application:
<customBinding>
<binding name="customHttpBinding_IBasoaWebService" sendTimeout="01:00:00">
<binaryMessageEncoding />
<httpCookieContainer />
<httpTransport maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" />
</binding>
</customBinding>
And this is the binding of the WCF service:
<basicHttpBinding>
<binding name="basicHTTP" closeTimeout="01:00:00" openTimeout="01:00:00"
maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
receiveTimeout="01:00:00" sendTimeout="01:00:00" allowCookies="false">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647"
maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="None" />
</binding>
</basicHttpBinding>
In the Silverlight application we are using this code to read the Set-Cookie header in the response:
IHttpCookieContainerManager cookieManager = channel.GetProperty<IHttpCookieContainerManager>();
if (cookieManager.CookieContainer == null)
cookieManager.CookieContainer = new CookieContainer();
Uri applicationUri = new Uri(Application.Current.Host.Source, "../");
string cookieString = cookieManager.CookieContainer.GetCookieHeader(applicationUri);
ParseCookieString(cookieString);
And this is the code to set the ASP.NET Session ID cookie in the request:
IHttpCookieContainerManager cookieManager = channel.GetProperty<IHttpCookieContainerManager>();
if (cookieManager.CookieContainer == null)
cookieManager.CookieContainer = new CookieContainer();
Uri applicationUri = new Uri(Application.Current.Host.Source, "../");
Cookie cookie = new Cookie("ASP.NET_SessionId", aspNetSessionId);
cookieManager.CookieContainer.Add(applicationUri, cookie);
Checking through Fiddler the messages which are exchanged I see that the WCF service sends correctly the Set-Cookie header in the first response, but the Silverlight is not able to read it. I have also tried to set the Cookie header in the request through the CookieContainer class, but with no luck. I cannot see it in Fiddler.
Coould someone give me an advice about what I must be doing wrong?
Many thanks in advance.
Jose Antonio Arroba

In a web application, I use URI to set the SessionID. (I think it's possible with a WCF service)
In the web.config of your service, define :
<system.web>
<sessionState cookieless="true"></sessionState>
</system.web>
or
<sessionState cookieless="UseUri"></sessionState>
The sessionId appaears like this :
http://localhost:51358/(S(1wnqb23d2qe4blfxzukligdo))/default.aspx
You can get and set the SessionID in the URI.

Finally I have been able to find a workaround to solve this problem.
The reason why the Cookie header was not sent with the requests was that I was setting a bad Uri in the Cookie object. If I am trying to connect to http://localhost:8080/Service, it seems taht the correct Uri value to provide is http://localhost:8080 and not http://localhost:8080/Service.
However I am still wondering how to read the Set-Cookie sent by the server. My workaround consist on sending the ASP.NET Session ID also in a custom SOAP header and read it in the client, which is IMHO rather redundant. But it works.

Related

Pass message headers on WCF via netTcpBinding

After banging my head for a day I'm asking for help. I have a Web Forms application with WCF service hosting at the same machine. The WCF service has methods with some sort of custom permissions (for instance, only "admin" can set passwords for users).
Web forms app uses Forms Authentication so I have HttpContext.Current.User with all the roles this uses has. So I used an AuthCookie to pass the object via HttpRequestMessageProperty and read it like this:
HttpRequestMessageProperty property =
OperationContext.Current.IncomingMessageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
With basicHttpBinding that worked fine. But then I needed to implement net.tcp protocol so the HttpRequestMessageProperty doesn't work anymore. I've tried to add it by MessageHeader but that doesn't work either.
OperationContext.Current.IncomingMessagePropertieshas got none of my headers...
So how can I pass the user roles list to my WCF? I am using Windows security like this but I need to pass roles from HttpContext.Current.User:
<netTcpBinding>
<binding transactionFlow="True">
<security mode="Transport">
<transport clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
OK so I found the working way for transferring the role info. Although this is still unclear why standard ways didn't work for me here is the right one (thanks to Guy Burstein):
Client:
MessageHeader<string> header = new MessageHeader<string>(roleData);
MessageHeader untypedHeader = header.GetUntypedHeader(ProjectParam.Role, "justASampleNamespace");
OperationContext.Current.OutgoingMessageHeaders.Add(untypedHeader);
WCF:
string roles = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(ProjectParam.Role, "justASampleNamespace");
if(roles.Contains(ProjectParam.AdminLoginID))
{
//Now business logic comes
}
ProjectParam.Role is just an internal constant utilized for key and the second parameter is a sample string (MSDN says the "namespace")

Could not find a base address that matches scheme https for the endpoint

<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="DataSoap" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<security mode="Transport" />
</binding>
</basicHttpBinding>
<customBinding>
<binding name="CustomBinding_GetData">
<binaryMessageEncoding />
<httpsTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="https://localhost/MyApp.Web/Webservice/Data.asmx"
binding="basicHttpBinding" bindingConfiguration="DataSoap"
contract="ServiceReference1.DataSoap" name="DataSoap" />
<endpoint address="https://localhost/MyApp.Web/Webservice/GetData.svc"
binding="customBinding" bindingConfiguration="CustomBinding_GetData"
contract="GetData.GetData" name="CustomBinding_GetData" />
</client>
</system.serviceModel>
Hello every one, above is my silverlight applications ServiceReferences.ClientConfig file. The site is configured to be accessed over https. From the above file, i would imagine i have everything configured correctly. I can browser to my service from local development environment successfully but after deploying the application in my QA environment, browing to the service gives me the error below.
Could not find a base address that matches scheme https for the endpoint with binding CustomBinding. Registered base address schemes are [http].
Any i dea why http is still being picked as the registered base address schemes only QA but not in my local development environment?.
EDIT:
#Brian, thanks for the reply, let me give you more information just in case it gives a much clear picture.
The site is configured for SSL, but the SSL certificate is installed on a load balancer which i have no access to.
Now from the error message, it would seem like i have to configure Host Headers and Secure Site Bindings in IIS, but can i really do this from IIS when the SSL certificate is installed and managed from the load balancer?
IT looks like the https binding are what is missing because i can reproduce the exact same error message from my development machine if i temporarily remove the https binding i created following this link.
http://weblogs.asp.net/scottgu/tip-trick-enabling-ssl-on-iis7-using-self-signed-certificates.
So would i be right to think that i need that https binding on the load balancer rather than in IIS because the site has no SSL certificate of its own in IIS?
I ran into this problem. Basically, the URL identity associated with the certificate doesn't match the URL of the website from which it comes ... at least that was my problem.
I was able to work around this client-side security check by specifically setting (in code) the System.ServiceModel.EndPointIdentity to the URL I was connecting to.
There's a CreateDNSIdentity() function to which you give the URL of the website you're hitting.
Here's a link to the MS documentation: http://msdn.microsoft.com/en-us/library/system.servicemodel.endpointidentity.creatednsidentity(v=vs.110).aspx
I'm not sure how you'd configure this without using code.
String sFullURL = "http://MyDNSServer:8001/SomeService"
String sDNS = "MyDNSServer";
System.ServiceModel.EndpointAddress Endpoint;
System.ServiceModel.EndpointIdentity Identity = default (System.ServiceModel.EndpointIdentity);
Identity = System.ServiceModel.EndpointIdentity.CreateDnsIdentity(sDNS);
EndPoint = new System.ServiceModel.EndpointAddress(new Uri(sFullURL), Identity);
UPDATE
OK, so imagine you had a web service and the public address for this web service was IP https:// 10.134.116.161:8001/MyService. The certificate below would pass the client-side cert verification check and you would not get an error. But if this certificate shown in the picture below is deployed on public URL https:// XZYCorp:8001/MyService, you'll get that error. So you either need to override the client side cert verification check or change the cert on the LB.

WCF - More than one endpoint configuration for that contract was found - Error

We have working ASP.Net web application with WCF. wcf service hosted as a windows service. All is fine. Then we made a change so that service contract will have different namespace (From Namespace1.IserviceContract to Namespace2.IserviceContract). After the change we deployed to the server and getting following error when we try to instantiate the service object.
System.InvalidOperationException: An endpoint configuration section for contract 'Namespace2.IserviceContract' could not be loaded because more than one endpoint configuration for that contract was found. Please indicate the preferred endpoint configuration section by name.
Generated: Fri, 06 Jul 2012 21:02:56 GMT
System.Web.HttpUnhandledException: Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.InvalidOperationException: An endpoint configuration section for contract 'Namespace2.IserviceContract' could not be loaded because more than one endpoint configuration for that contract was found. Please indicate the preferred endpoint configuration section by name.
at System.ServiceModel.Description.ConfigLoader.LookupChannel(String configurationName, String contractName, Boolean wildcard)
at System.ServiceModel.Description.ConfigLoader.LoadChannelBehaviors(ServiceEndpoint serviceEndpoint, String configurationName)
at System.ServiceModel.ChannelFactory.ApplyConfiguration(String configurationName, Configuration configuration)
at System.ServiceModel.ChannelFactory.ApplyConfiguration(String configurationName)
at System.ServiceModel.ChannelFactory.InitializeEndpoint(String configurationName, EndpointAddress address)
at System.ServiceModel.ChannelFactory`1..ctor(String endpointConfigurationName, EndpointAddress remoteAddress)
at System.ServiceModel.EndpointTrait`1.CreateSimplexFactory()
at System.ServiceModel.EndpointTrait`1.CreateChannelFactory()
at System.ServiceModel.ClientBase`1.CreateChannelFactoryRef(EndpointTrait`1 endpointTrait)
at System.ServiceModel.ClientBase`1.InitializeChannelFactoryRef()
at System.ServiceModel.ClientBase`1..ctor()
at TestApplication.ManagementWrapper.VerifyAuthentication(Int32 appId, String Token)
at TestApplication.VerifyAuthentication(String tokenstring)
we did a research about this issue and found that this type if exception shows up if we have two client endpoints defined in our web.config file. however we are certain that we have only one client endpoint defined. More over this exception shows up only in the server. local works fine. here is our service model:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_Management" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="4194304" maxBufferSize="2147483647" maxConnections="10" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="32768" maxNameTableCharCount="2147483647" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://servername:9010/Management/service/ManagementService" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_Management" contract="Namespace2.IserviceContract" name="NetTcpBinding_IserviceContract" />
</client>
</system.serviceModel>
we also tried to restart IIS and application pool. Still getting the same exception.
Try searching your web.config for another that is using the web address as your ManagementService. Also, search the web.config for any reference to the old namespace (contract="Namespace1.IserviceContract"). Don't forget to check for extra .config files. That little gotcha has burned me before.
Whatever protocol is being called like basic, net.tcp or wshttp, that address should be in web config file remove other addresses from client section in app.config file, in my case i am calling the service as htp://machinename:700/test.svc but in the client section there were addresses with net.tcp and wshttp configurations, removed those addresses and issue is fixed for me.
Please right click on svc file of your wcf service and click on View markup.
then modify namespace there also. It should work fine then.
If everything in your web.config appears to be correct, this error can be caused by another application on the same server. I spent several days troubleshooting a similar issue.
In my case, the environment had a large number of WCF services deployed as web applications in IIS under a single website as follows.
/Root Website
/Service1
/Service2
/Service3
/ServiceX
One of the child services was mistakenly deployed to the root website physical folder rather than to it's own physical folder. This bad deployment contained a client endpoint definition that was common to all of the services and caused all of the child services to break. Apparently, the same client endpoint cannot be used by the parent website and a child web application.
Removing the client endpoint from the root website fixed the issue for me.

How do I get Authentication cookies in all subsequent HTTP Request headers to a WCF service?

I am having troubles getting authentication cookies placed into the HTTP Request Headers of a client Windows Service consuming a WCF Service hosted in IIS that is using ‘Sessions’. The client app uses Forms Authentication to gain access to the WCF Service. After a successful Authentication, I capture the HTTP Response header contains a Set-Cookie for .ASPXAUTH and a Set-Cookie for ASP.NET_SessionId. I add these cookies to the HTTP Request header for all subsequent requests to the WCF Service. Using the Fiddler debugging tool I can see that for every request to the WCF service, two HTTP Requests are sent. The first HTTP Request header does not contain the cookies but the second HTTP Request does. When I secure the service by setting the Authorization section of the web.config to ‘deny users=”?”’, the first HTTP Request without the cookies in the header forces a ‘Redirect’ back to login preventing access to the service. The first request I believe to be related to the use of ‘Sessions’. When I disable ‘Sessions’ in my WCF Service, I get only one HTTP Request per WCF call and the cookies are in the header. How do I get the cookies returned from Authentication in all of the HTTP Request headers sent to my WCF Service?
Any help with this issue would be very much appreciated.
I have included my bindings from the client.
<customBinding>
<binding name="CustomBinding_IMySyncService" receiveTimeout="00:01:00"
sendTimeout="00:01:00">
<reliableSession acknowledgementInterval="00:00:30"
inactivityTimeout="00:03:00"
maxTransferWindowSize="4096" />
<binaryMessageEncoding maxReadPoolSize="2147483647" maxWritePoolSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
</binaryMessageEncoding>
<httpsTransport manualAddressing="false" maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647" allowCookies="false" authenticationScheme="Anonymous"
bypassProxyOnLocal="false" decompressionEnabled="true" hostNameComparisonMode="StrongWildcard"
keepAliveEnabled="true" maxBufferSize="2147483647" proxyAuthenticationScheme="Anonymous"
realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false"
useDefaultWebProxy="true" />
</binding>
<binding name="CustomBinding_AuthenticationService">
<binaryMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</binaryMessageEncoding>
<httpsTransport manualAddressing="false" maxBufferPoolSize="524288"
maxReceivedMessageSize="65536" allowCookies="false" authenticationScheme="Anonymous"
bypassProxyOnLocal="false" decompressionEnabled="true" hostNameComparisonMode="StrongWildcard"
keepAliveEnabled="true" maxBufferSize="65536" proxyAuthenticationScheme="Anonymous"
realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false"
useDefaultWebProxy="true" />
</binding>
</customBinding>
I guess by session you mean reliable session allowed in your binding. Reliable session sends infrastructure messages which are out of your control. The modifying that behavior with demand some modification on very low level of WCF channel stack.
If you want to use ASP.NET session handled by cookies you have to use ASP.NET compatibility mode. Even with ASP.NET compatibility WCF doesn't suppose that authentication will be handled by cookies - the correct way is authenticating each request. WCF also by default doesn't use cookies at all.
ASP.NET compatibility mode is supposed to be used with plain web services - as backward compatibility with old ASMX web services. So it doesn't have to work with more advanced protocols like WS-Reliable Session. Use either reliable session or ASP.NET compatibility - not both.
Once you are using reliable session you already have WCF session (the single service instance handles all request from the same client proxy instance) so you don't need ASP.NET session. If you use built-in WCF security pipeline authentication credentials should be send by the proxy automatically within each message.

implementing Ws-security within WCF proxy

I have imported an axis based wsdl into a VS 2008 project as a service reference.
I need to be able to pass security details such as username/password and nonce values to call the axis based service.
I have looked into doing it for wse, which i understand the world hates (no issues there)
I have very little experience of WCF, but have worked how to physically call the endpoint now, thanks to SO, but have no idea how to set up the SoapHeaders as the schema below shows:
<S:Envelope
xmlns:S="http://www.w3.org/2001/12/soap-envelope"
xmlns:ws="http://schemas.xmlsoap.org/ws/2002/04/secext">
<S:Header>
<ws:Security>
<ws:UsernameToken>
<ws:Username>aarons</ws:Username>
<ws:Password>snoraa</ws:Password>
</ws:UsernameToken>
</wsse:Security>
•••
</S:Header>
•••
</S:Envelope>
Any help much appreciated
Thanks, Mark
In order to call these kind of services, you will typically use either basicHttpBinding (that's SOAP 1.1 without WS-* implementations) or then wsHttpBinding (SOAP 1.2, with WS-* implementations).
The main issue will be getting all the security parameters right. I have a similar web service (Java-based) that I need to call - here's my settings and code:
app./web.config
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="SoapWithAuth" useDefaultWebProxy="false">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" proxyCredentialType="None" realm="" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint name="SoapWithAuth"
address="http://yourserver:port/YourService"
binding="basicHttpBinding"
bindingConfiguration="SoapWithAuth"
contract="IYourService" />
</client>
</system.serviceModel>
and then in your client's code when calling the service, you need this snippet of code:
IYourServiceClient client = new IYourServiceClient();
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "top-secret";
Does that help at all?
The WCF client proxy doesn't support the password digest option. The only way to do this is to build the UsernameToken yourself and then inject it into the SOAP headers before the message is sent.
I had a similar problem which is described here, which should be enough to help you solve your same issue.
I ended up using the old WSE3.0 library for the UsernameToken, rather than coding the hashing algorithm myself and then using a custom behavior to alter the SOAP headers.

Resources