Exposing WCF Service to Mobile Clients - silverlight

I have an existing ASP.NET web application. This ASP.NET web application uses JQuery to provide a rich experience to the users. This user interface interacts with the server through some WCF services. A sample service looks like the following:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(IncludeExceptionDetailInFaults = false)]
public class myService : ImyService
{
public bool SomeMethod(string parameter1, string parameter2)
{
try
{
return true;
}
catch (Exception ex)
{
return false;
}
}
}
I now want to expose this service to an iPhone and a Windows Phone 7 application. In an attempt to do this, I have configured the service like the following:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="myServiceBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="myService">
<endpoint address="" behaviorConfiguration="myServiceBehavior"
binding="webHttpBinding" contract="ImyService" />
</service>
</services>
</system.serviceModel>
The service works with the JQuery calls in my ASP.NET web application. I have not begun working on the iPhone client. But, when I try to expose this service to my WP7 client, I run into problems. As it stands now, when I launch my WP7 application, I receive an error that says:
KeyNotFoundException
If I change the binding in the config file to "basicHttpBinding", I cannot reference the service in Visual Studio. I receive an error that says:
The endpoint at 'http://machine:80/services/myService.svc' does not have a Binding with the None MessageVersion.  'System.ServiceModel.Description.WebScriptEnablingBehavior' is only intended for use with WebHttpBinding or similar bindings.
Ugh. How do I move forward? I thought WCF was designed to make this stuff easier. But I feel like I'm getting stuck doing something relatively basic.
Thank you for your help!

As far as I know (but I have no experience at all with iPhone or WinPhone development) you should be able to expose your WCF service using the regular webHttpBinding and no web script functionality, to get a normal REST style WCF service:
<system.serviceModel>
<services>
<service name="myService">
<endpoint
address=""
binding="webHttpBinding"
contract="ImyService" />
</service>
</services>
</system.serviceModel>
That alone should be sufficient - just browse to the virtual directory where your *.svc file is located and you should see your top-level resources in your browser.
The mobile devices typically don't support the SOAP-style bindings (like basicHttpBinding and so on) - so you need to use webHttpBinding instead (since that really only requires an HTTP client stack on the device - and every device these days definitely has this!)

Related

how to call web service using NTLM authorization scheme?

I'm a noob to calling WCF web services, so am hoping this is an easy question. When calling a web service with .NET 4 winform client, how do I change the authorization scheme from Anonymous to NTLM?
Right now I'm getting the exception: The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'.
My goal is to build a little tool to help me monitor TFS 2010's data warehouse and cube. TFS provides a WarehouseControlWebService web service. I can call the service via Test mode in a browser when logged on to the server. However I'm trying to call the same web service remotely, from my desktop. My user account is in the local Administrators group on the server.
I've created a .NET 4 WinForm with the canonical Button1 and TextArea1. I then added a service reference to the web service and creatively called it ServiceReference1:
Add Service Reference...
http://tfssvr:8080/tfs/TeamFoundation/Administration/v3.0/WarehouseControlService.asmx
And here's my code behind:
private void button1_Click(object sender, EventArgs e)
{
// Creating a proxy takes about 3-4 seconds
var dwSvc = new ServiceReference1.WarehouseControlWebServiceSoapClient();
// Invoking the method throws an MessageSecurityException
var dwStatus = dwSvc.GetProcessingStatus(null, null, null);
}
I'm getting System.ServiceModel.Security.MessageSecurityException:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'.
I've tried passing my credentials via:
dwSvc.ClientCredentials.Windows.ClientCredential =
new System.Net.NetworkCredential("user", "pass", "domain");
and also ...
dwSvc.ClientCredentials.Windows.ClientCredential =
CredentialCache.DefaultNetworkCredentials;
I'm wading through the WCF documentation but ... oh boy ... there's a lot there. I'm hoping this is something easy??
Thanks in advance.
Set your config bindings
to security mode="TransportCredentialOnly" and transport clientCredentialType="Ntlm"
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="WarehouseControlWebServiceSoap" 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="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://tfsServer:8080/tfs/TeamFoundation/Administration/v3.0/WarehouseControlService.asmx"
binding="basicHttpBinding" bindingConfiguration="WarehouseControlWebServiceSoap"
contract="TfsWarehouse.WarehouseControlWebServiceSoap" name="WarehouseControlWebServiceSoap" />
</client>
</system.serviceModel>
You are looking in the right direction. This is a good page with some example level information on available authentication methods you need: http://man.ddvip.com/web/bsaspnetapp/LiB0087.html. At least that page should give you some more clues to continue your efforts on.

Accessing a Claim Aware WCF from Silverlight

I am working on a Silverlight 4 application which connects to a claim ware WCF Service. I am using the following code to retrive the claim token in my WCF to perform authorization.
IClaimsPrincipal principal = ( IClaimsPrincipal )Thread.CurrentPrincipal;
IClaimsIdentity identity = ( IClaimsIdentity )principal.Identity;
return string.Format( "You entered: {0} and you are {1}", value, identity.Name );
When I use wsHttpBinding in WCF and try it out with a console app, it works fine. But since Silverlight only supports basicHttp and customeBinding, i cannot use wsHttp, ws2007Http or anyother binding. Becase of which I am not getting the IClaimIdentity token in my WCF from Silverlight.
Is there any way I can use any of the Silverlight suppported binding and still get the ClaimIdentity in my WCF. Is there any tutorial/help text where I can read more abouth this.
My WCF settings are:
<system.serviceModel>
<services>
<service name="ClainAwareWCF.Service" behaviorConfiguration="ClainAwareWCF.ServiceBehavior">
<endpoint address="" binding="basicHttpBinding" contract="ClainAwareWCF.IService" bindingConfiguration="basicbind">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="basicbind">
<security mode="TransportCredentialOnly"></security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="ClainAwareWCF.ServiceBehavior" >
<federatedServiceHostConfiguration/>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="federatedServiceHostConfiguration" type="Microsoft.IdentityModel.Configuration.ConfigureServiceHostBehaviorExtensionElement, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</behaviorExtensions>
</extensions>
</system.serviceModel>
Trying to call this directly from the client will never happen because of the binding issues and also because the client auth awareness security of SL (Windows/Forms/WIF/etc.), but one approach is to use RIA Services Domain Authentication Services to authenticate and call the services from the server-side through a WCF RIA Invoke endpoint. The user's security context is proxied to the client and you can tunnel data over the wire in a straight-forward manner.
This may get you in the right direction:
http://archive.msdn.microsoft.com/RiaServices/Release/ProjectReleases.aspx?ReleaseId=5617
Silverlight doesn´t support Claims Based Authorization and WS-Trust out of the box. Microsoft was going to put this into Silverlight 5 but forgot to do so unfortunately.
There is however a very elegant and usable "Silverlight" version of the WIF IdentityModel stuff available in the Identity Training Kit.
The solution consists of a base AuthenticationService that translates WIF authentication tokens to claims server side, and a Silverlight client library "SL.IdentityModel" containing the building blocks such as a Silverlight version of a ClaimsPrincipal.
Get the Identity Training Kit here. Look for the sample Silverlight implementation.

Making a Silverlight WCF SOAP service secured with UserNameOverTransport work over HTTP for network architecture

I have a Silverlight 4 application that uses a WCF SOAP service. The authentication/authorization happens per call (quasi RESTful). This is done by using authenticationMode=UserNameOverTransport - this basically means that that username/password is in each WCF call, but is protected by the SSL encryption of each message. The great thing about this scheme is that I can configure a membership provider in my web.config to do the authentication, making it flexible for different installations.
I have a client that would like to set this website up on their network where the scheme is: Internet <= SSL Traffic => External facing SSL enabled forwarding server <= unsecure HTTP in their internal network => server that hosts my application. They assure me this is a common architecture and I believe them, I am not that experienced an internet application developer.
I am not sure what to do about this as my application is set up to be on the SSL enabled server (UserNameWithTransport is over SSL). in plain HTTP I am not sure how I would get the username which I need to provide the user specific application data. WCF does not provide a "UserNameWithNoTransport" authenticationMode as that would mean sending the username/password in plain text, which is silly. Right now my server side code gets the user from the ServiceSecurityContext.Current.PrimaryIdentity.Name, knowing that the web server has already taken care of the SSL encryption and user authentication. How can I have this work in a way that makes sense in an HTTP solution?
I would like a solution that allows me to configure my solution to work in both the HTTP and HTTPS situation from the web.config, if this is not possible than any other advice is appreciated. thanks
I will place a bounty on this question in a few days, if you give a good answer before then you'll get it.
EDIT: here is the web config as requested:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<system.web>
<pages controlRenderingCompatibilityVersion="4.0" clientIDMode="AutoID"/>
<membership defaultProvider="SampleProvider">
<providers>
<add name="SampleProvider" type="MyNamespace.NullMembershipProvider, MyDLL"/>
</providers>
</membership>
</system.web>
<appSettings>
...
</appSettings>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<bindings>
<customBinding>
<binding name="BinaryCustomBinding" sendTimeout="00:10:00">
<security authenticationMode="UserNameOverTransport"/>
<binaryMessageEncoding />
<httpsTransport maxBufferSize="100000" maxReceivedMessageSize="100000" />
</binding>
</customBinding>
</bindings>
<services>
<service name="MyNamespace.MyService">
<endpoint
binding="customBinding" bindingConfiguration="BinaryCustomBinding"
name="MyService" contract="MyNamespace.IServiceContract" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata
httpsGetEnabled="true"
httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="MembershipProvider"
membershipProviderName="SampleProvider"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<log4net>
...
</log4net>
</configuration>
Allowing UserNameOverTransport over HTTP is possible in .NET 4 but I think it is not possible in Silverlight. You need to set allowInsecureTransport attribute of security element:
<customBinding>
<binding name="BinaryCustomBinding" sendTimeout="00:10:00">
<security authenticationMode="UserNameOverTransport" allowInsecureTranposrt="true"/>
<binaryMessageEncoding />
<httpTransport maxBufferSize="100000" maxReceivedMessageSize="100000" />
</binding>
</customBinding>
The problem is that allowInsecureTranposrt is not available in Silverlight. Without this you can't use UserName token over unsecured channel.

Silverlight + WCF commnication exception

I have a silverlight application which has a WCF in it. when the wcf is called it fails with this exception:
An error occurred while trying to make a request to URI 'http://localhost:4693/MapService.svc'. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent. This error may also be caused by using internal types in the web service proxy without using the InternalsVisibleToAttribute attribute. Please see the inner exception for more details.
Both silverlight and WCF are running from local.
My silverlight is running from:
...\SilverlightApplication1\SilverlightApplication1\Bin\Release\SilverlightApplication1TestPage.html
this is the WCF web.config services tag:
<services>
<service behaviorConfiguration="FileUpAndDownload.Web.MapServiceBehavior" name="FileUpAndDownload.Web.MapService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="MapBinding" contract="FileUpAndDownload.Web.IMapService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
and this is the ServiceReference.ClientConfig:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IMapService" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:4693/MapService.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IMapService" contract="MapService.IMapService"
name="BasicHttpBinding_IMapService" />
</client>
</system.serviceModel>
I've placed the clientaccesspolicy.xml in bin\release but doesn't help.
HELP PLEASE!
You need some extra configuration to do a cross domain call see:http://www.dotnetcurry.com/ShowArticle.aspx?ID=208&AspxAutoDetectCookieSupport=1
You probably have the clientaccesspolicy.xml in the wrong place, check your IIS log for file not found errors.
The client access policy file must be in your wwwroot folder
There are issues with running Silverlight locally as opposed to from a web server.
The access permissions are different and Silverlight won't be able to access ANY webservice.
I ran into that issue somne time ago with SL2 I think, it might have changed.
You should create a new WebApplication project in Visual Studio and host Silverlight in it.
Then you SL client will have no problem connecting to the service.

How to call webservice in Silverlight which is added to Cloud Service Project?

I would like to ask a question. One of my ex-colleague wrote one window azure project and now I need to continue this project. Web Service is in that window azure project and I need to call that web service in silverlight. Therefore, I add new silverlight project at that existing Window azure project. And when I am trying to add Service Reference in silverlight application. It shows "No Endpoint compatible with Silverlight3 were found" and it cannot create ServiceReference.config file.
I am not too family with web service and c#. So , could you tell me step by step of using web service at Silverlight.
I also tried to change "basicHttpBinding" and "customBinding" in Web.config file. But it doesn't allow me to change anything. So, without changing anything at serverside, how can I call webservice in silverlight?
Here is some part of code.
IEventHandler.cs
`[OperationContract]
[WebGet(UriTemplate = "Holidays", ResponseFormat = WebMessageFormat.Xml)]
List<Holidays> GetHolidays();`
EventHandler.svc
`public List<Holidays> GetHolidays()
{
//database declaration for purpose of accessing the db.
Database db = new Database();
//dg.getHolidays is found in the Database class.
List<Holidays> holidays = db.getHolidays();
return holidays;
}`
Web.config
`<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webBinding"
maxBufferPoolSize="2147483647"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647" >
<readerQuotas
maxArrayLength="2147483647"
maxStringContentLength="2147483647"/>
<!--<security mode="Transport">
</security>-->
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="webBehavior">
<webHttp />
<enableWebScript/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="Rest_EventHandler_WebRole.EventHandlerBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="Rest_EventHandler_WebRole.EventHandlerBehavior"
name="Rest_EventHandler_WebRole.EventHandler">
<endpoint address="" behaviorConfiguration="webBehavior" binding="webHttpBinding"
bindingConfiguration="webBinding" contract="Rest_EventHandler_WebRole.IEventHandler">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>`
For silverlight, how can I call that WebService? I don't have permission to change the serverside part. Do I need to use sisvcutil.exe? Could you kindly help me my problem?
I don't think you can do this. Silverlight only supports basicHttpBinding and customBinding. I suppose one option would be to create a middle WCF service layer where you just call the azure service and pass the result back to Silverlight.
Silverlight client -> Middle WCF layer -> Azure service
But this seems unnecesssary and overkill. Is there no way you can get control over the web service?

Resources