Enabling cross-domain calls for Silverlight apps on self-hosted web services

In order for a Silverlight (or Flash) app coming from one domain to be able to consume data from services in a different domain, the service must "allow" the app to do so by providing a policy file which grants access (to prevent all sorts of cross-site scripting attacks). This policy file must be located in the root of the "domain" (hostname + port), so if your service is located at https://my.service.com:8000/Service/CoolService.svc/Endpoint, the policy file must be located at https://my.service.com:8000/ClientAccessPolicy.xml (or https://my.service.com:8000/crossdomain.xml in case of the Flash format). That's fairly easy to do on a IIS-hosted service (simply put the static policy file in the root of the web), but for self-hosted apps it isn't as simple (there's no "root" of the web).

To solve this problem for self-hosted WCF services, you can use the web programming model support fairly easily. Basically, you'd define the base address at the root of the domain, and have a web endpoint at the "" address. All the "real" service endpoints would then be in different addresses. The example below shows it in action:

[update (2010/07/24) : if your self-hosted service uses the TCP binding (new in SL4), you can look at the solution at my new post at https://blogs.msdn.com/b/carlosfigueira/archive/2010/07/25/enabling-cross-domain-calls-for-sl-apps-on-self-hosted-tcp-services.aspx]

A complete VS solution with a SL application and the self-hosted service can be found at the MSDN Code Gallery at https://code.msdn.microsoft.com/Accessing-self-hosted-WCF-7872c931. The code for the service itself is listed below.

  1.     public class SelfHostedServiceWithSilverlightPolicy
  2.     {
  3.         [ServiceContract]
  4.         public interface ITest
  5.         {
  6.             [OperationContract]
  7.             string Echo(string text);
  8.         }
  9.         [ServiceContract]
  10.         public interface IPolicyRetriever
  11.         {
  12.             [OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
  13.             Stream GetSilverlightPolicy();
  14.             [OperationContract, WebGet(UriTemplate = "/crossdomain.xml")]
  15.             Stream GetFlashPolicy();
  16.         }
  17.         public class Service : ITest, IPolicyRetriever
  18.         {
  19.             public string Echo(string text) { return text; }
  20.             Stream StringToStream(string result)
  21.             {
  22.                 WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
  23.                 return new MemoryStream(Encoding.UTF8.GetBytes(result));
  24.             }
  25.             public Stream GetSilverlightPolicy()
  26.             {
  27.                 string result = @"<?xml version=""1.0"" encoding=""utf-8""?>
  28. <access-policy>
  29.     <cross-domain-access>
  30.         <policy>
  31.             <allow-from http-request-headers=""*"">
  32.                 <domain uri=""*""/>
  33.             </allow-from>
  34.             <grant-to>
  35.                 <resource path=""/"" include-subpaths=""true""/>
  36.             </grant-to>
  37.         </policy>
  38.     </cross-domain-access>
  39. </access-policy>";
  40.                 return StringToStream(result);
  41.             }
  42.             public Stream GetFlashPolicy()
  43.             {
  44.                 string result = @"<?xml version=""1.0""?>
  45. <!DOCTYPE cross-domain-policy SYSTEM ""https://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"">
  46. <cross-domain-policy>
  47.     <allow-access-from domain=""*"" />
  48. </cross-domain-policy>";
  49.                 return StringToStream(result);
  50.             }
  51.         }
  52.         public static void Main()
  53.         {
  54.             string baseAddress = "https://" + Environment.MachineName + ":8000";
  55.             ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
  56.             host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "basic");
  57.             host.AddServiceEndpoint(typeof(IPolicyRetriever), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
  58.             ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
  59.             smb.HttpGetEnabled = true;
  60.             host.Description.Behaviors.Add(smb);
  61.             host.Open();
  62.             Console.WriteLine("Host opened");
  63.  
  64.             Console.WriteLine("Press ENTER to close");
  65.             Console.ReadLine();
  66.             host.Close();
  67.         }
  68.     }