How to consume a self-hosted WCF service in a cross-domain environment by Silverlight client

Background:

When a Silverlight client tries to consume WCF service, it’s allowed only site-of-origin communication by default. For example, a Silverlight control hosted at https://domainA/default.aspx can only consume only services on that same domain by default, that is: https://domainA/WCFService.svc, but not https://domainB/WCFservice.svc.

To make the WCF service hosted at : https://domainB/WCFservice.svc be accessible for Silverlight hosted at https://domainA/default.aspx, we need to configure a opt-in cross-domain policy explicitly which is called clientaccesspolicy.xml or crossdomain.xml. There are many articles addressing this topic:

 

Making a Service Available Across Domain Boundaries

https://msdn.microsoft.com/en-us/library/cc197955(VS.95).aspx

Warning: Could not locate cross-domain policy at '<URL>'

https://msdn.microsoft.com/en-us/library/cc838225(VS.95).aspx

When our WCF service is hosted in IIS, we only need to deploy the clientaccesspolicy.xml at the root folder where the WCF service is published. For example: if our WCF service is published as: https://domainB/WCFservice.svc, then the cross-domain policy should be accessible by https://domainB/clientaccesspolicy.xml.

However, it will be a little different for the self-hosted WCF service. In this article, we will demonstrate how to consume self-hosted WCF service in a cross-domain environment by Silverlight client. At the same time, as NET.TCP binding is supported by Silverlight4 now, the steps will be totally different for NET.TCP Binding compared to basicHttpBinding. In this article, I would like to introduce the steps for both BasicHttpBinding scenario and Net.TCP binding scenario.

How to consume a self-hosted WCF service

l Scenario 1: Self-hosted WCF service with basicHttpBinding.

Ø Step1:

Define an interface “IPolicyRetriever” :
[ServiceContract]

    public interface IPolicyRetriever

    {

        [OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]

        Stream GetSilverlightPolicy();

        [OperationContract, WebGet(UriTemplate = "/crossdomain.xml")]

        Stream GetFlashPolicy();

}

Ø Step 2:

Implement above interface in your WCF service:

public class WCFService:IWCFService,IPolicyRetriever

    {

       //IWCFService implementation

                                …

                                …

//IPolicyRetriever implementation

    private Stream StringToStream(string result)

        {

            WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";

            return new MemoryStream(Encoding.UTF8.GetBytes(result));

        }

        public Stream GetSilverlightPolicy()

        {

            string result = @"<?xml version=""1.0"" encoding=""utf-8""?>

            <access-policy>

            <cross-domain-access>

             <policy>

                <allow-from http-request-headers=""*"">

         <domain uri=""*""/>

            </allow-from>

            <grant-to>

                <resource path=""/"" include-subpaths=""true""/>

            </grant-to>

        </policy>

        </cross-domain-access>

        </access-policy>";

            return StringToStream(result);

     }

Ø Step 3:

Publish an endpoint which address is “ ” based on the host address of HTTP schema in order to accept the request for clientaccesspolicy.xml. We can achieve this by programmatically or administratively.

Programmatically:

Type serviceType = typeof(WCFService);

ServiceHost host = new ServiceHost(serviceType);

host.AddServiceEndpoint(typeof(IPolicyRetriever), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());

 

Administratively:

<behaviors>

    <endpointBehaviors>

       <behavior name="WebHttpNewBehavior">

          <webHttp />

            </behavior>

    </endpointBehaviors>

           ...

 </behaviors>

 <services>

       <service behaviorConfiguration="NewBehavior">

                …

          <endpoint behaviorConfiguration="WebHttpNewBehavior" binding="webHttpBinding"

                    bindingConfiguration="" name="PolicyEndpoint" contract="WCFService.IPolicyRetriever" />

                ...

        </service>

 </services>

Upon above steps, when a Silverlight control sends out a Request for cross-domain self-hosted WCF service, this server of service side should be able to retrieve its client access policy and then decide if this request is authorized.

l Scenario 2: Self-hosted WCF service with NET.TCP binding.

 

Although it’s declared that NET.TCP binding is supported by Silverlight 4 as follows:

WCF NET.TCP Protocol in Silverlight 4

https://www.silverlightshow.net/items/WCF-NET.TCP-Protocol-in-Silverlight-4.aspx

Before starting our steps, I’d like to highlight some points which are different with basicHttpBinding scenario.

1. Actually NET.TCP schema is not recognized by Silverlight 4. At least currently it is. In order to use NET.TCP binding, it’s needed that we use customBinding.

2. TCP Port range is limited within 4502 – 4534, which means that your WCF service must be published within above port range so that the Silverlight client can access it.

In order to make the client access policy be accessible, TCP port 943 is used to expose policy dedicatedly before Silverlight4 RC.

For more details, you can reference to below article:

Network Security Access Restrictions in Silverlight

https://msdn.microsoft.com/en-us/library/cc645032(VS.95).aspx

To make our life easier, Tomasz Janczuk from Microsoft has created a free template for console applications that serves like TCP socket policy server.

However, the net.tcp protocol in Silverlight 4 RC expects the socket policy to be available over the HTTP protocol at the root of the domain as opposed to over TCP at port 943 as before.

You can reference to below article about this change:

Pub/sub sample with WCF net.tcp protocol in Silverlight 4

https://tomasz.janczuk.org/2009/11/pubsub-sample-with-wcf-nettcp-protocol.html

Here, I’d like to demonstrate the steps for Silverlight 4 RC scenario, which means that the socket policy is exposed over the HTTP protocol at the root of the domain.

Ø Step 1:

Configure WCF service to use customBinding, and a metadata exchange endpoint based on NET.TCP schema as well. You can reference to below configuration:

<bindings>

        <customBinding>

          <binding name="NewBinding0">

            <binaryMessageEncoding />

            <tcpTransport />

          </binding>

        </customBinding>

 </bindings>

<services>

 <service behaviorConfiguration="NewBehavior" name="Demo.WCFService">

    <endpoint address="WCFService" binding="customBinding" bindingConfiguration="NewBinding0"

                    name="ServiceEndpoint" contract=" Demo.IWCFService " />

    <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""

                    name="MEXEndpoint" contract="IMetadataExchange" />

    <host>

       <baseAddresses>

               <add baseAddress="net.tcp://[domainB]:4503/" />

        </baseAddresses>

    </host>

   </service>

 </services>

Ø Step 2:

Deploy the clientaccesspolicy.xml at the root folder of https://domainB  to make sure that the clientaccesspolicy.xml be accessible via https://domainB/clientaccesspolicy.xml. The clientaccesspolicy.xml should look like as follows:

<access-policy>

  <cross-domain-access>

    <policy>

      <allow-from>

        <domain uri="*"/>

      </allow-from>

      <grant-to>

        <socket-resource port="4502-4534" protocol="tcp" />

      </grant-to>

    </policy>

  </cross-domain-access>

</access-policy>

After above steps, your Silverlight should be able to call the self-hosted WCF service with NET.TCP binding.

Best Regards,

Winston.