| Comments

Now that Silverlight 2 has been released, one of the features (or should I say fixes) that is included is the ability for non-secure applications to call secure services.  Previously this was not allowed and we referred to it as “cross-scheme violation.”  That means that a particular protocol scheme (file, http, https) could not access another.  Prior to release this meant that a XAP hosted in an HTTP context could not call a secure service.  Now we no longer have that restriction with the release.  There are some things you have to do, so let me take a brief moment to demonstrate.

First, let’s assume this environment:

We have our application that is served from our web server via HTTP.  so our site is hosting the app on http://foo.com/MyApp.XAP.  Our service is hosted on a secure endpoint which represents our segmented API. 

NOTE: For sake of simplicity and lack of “real” hardware to actually segment out a totally representative environment.  Translated: I only have one SSL certificate and wanted to keep hosting simple…the important note is that we have two separate domains.

And what we have created here is a double-whammy – a cross-domain, secure service request.  So even though when you look at the diagram above, you might ask isn’t the client machine making the request?  Yes, but from an application with a different source-of-origin, which is what creates this scenario.  Let’s continue, shall we?

Prior to release, this simply wasn’t enabled yet.  Now that it is enabled, we can have our MyApp.xap application call our secure service (in this example is a SOAP service hosted in ASP.NET).  In Silverlight 2, the service owner still has to opt-in for this to occur.  We make use of the clientaccesspolicy.xml file which is leveraged for cross-domain scenarios (more information about cross-domain and policy files can be found here including a code snippet for Visual Studio to generate them).  In our case here we need that to support our cross-domain scenario anyway, but we’re also going to leverage it to enable non-secure callers to our service. 

Let me make that clear:  even if your secure service was hosted on the same domain as your non-secure caller, you would still need a policy file in place to enable non-secure callers.

What does the clientaccesspolicy.xml file look like then?  Here’s what we’re using for our application:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <access-policy>
   3:   <cross-domain-access>
   4:     <policy>
   5:       <allow-from http-request-headers="SOAPAction">
   6:         <domain uri="http://*"/>
   7:         <domain uri="https://*" />
   8:       </allow-from>
   9:       <grant-to>
  10:         <resource include-subpaths="false" path="/"/>
  11:       </grant-to>
  12:     </policy>
  13:   </cross-domain-access>
  14: </access-policy>

Now that this is in place, we’re done.  Notice the two (2) domain nodes there?  I couldn’t have just put “*” in there as we require you to explicitly allow non-secure callers in this scenario.  And once you do that, if you didn’t add the secure callers (https://*) then you’d leave them out.  So although it looks a little weird (as if to say Tim, in my mind that reads “*”), it is correct.  That’s right, there isn’t anything more you need to do.  Our code would look the same.  When we add a service reference to our Silverlight application, you’ll see that the ServicesReferences.clientconfig makes note of the secure transport:

   1: <configuration>
   2:     <system.serviceModel>
   3:         <bindings>
   4:             <basicHttpBinding>
   5:                 <binding name="fooSoap" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
   6:                     <security mode="Transport" />
   7:                 </binding>
   8:             </basicHttpBinding>
   9:         </bindings>
  10:         <client>
  11:             <endpoint address="https://www.timheuer.com/foo.asmx" binding="basicHttpBinding"
  12:                 bindingConfiguration="fooSoap" contract="Bar.fooSoap" name="fooSoap" />
  13:         </client>
  14:     </system.serviceModel>
  15: </configuration>

Here’s the rest of the code I’m using in XAML:

   1: <UserControl x:Class="XDomain.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     Width="300" Height="300">
   5:     <Grid x:Name="LayoutRoot" Background="White">
   6:         <StackPanel>
   7:             <TextBox x:Name="MyName" FontSize="24" />
   8:             <Button Content="Repeat" FontSize="24" Width="150" Height="50" 
   9:                     Click="Button_Click" />
  10:             <TextBlock FontSize="24" x:Name="RepeatedName" />
  11:         </StackPanel>
  12:     </Grid>
  13: </UserControl>

and the code for the event handler on the button:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Net;
   5: using System.Windows;
   6: using System.Windows.Controls;
   7: using System.Windows.Documents;
   8: using System.Windows.Input;
   9: using System.Windows.Media;
  10: using System.Windows.Media.Animation;
  11: using System.Windows.Shapes;
  12:  
  13: namespace XDomain
  14: {
  15:     public partial class Page : UserControl
  16:     {
  17:         public Page()
  18:         {
  19:             InitializeComponent();
  20:         }
  21:  
  22:         private void Button_Click(object sender, RoutedEventArgs e)
  23:         {
  24:             Bar.fooSoapClient foo = new XDomain.Bar.fooSoapClient();
  25:             foo.SayMyNameCompleted += (sdr, args) =>
  26:                 {
  27:                     RepeatedName.Text = args.Result;
  28:                 };
  29:             foo.SayMyNameAsync(MyName.Text);
  30:         }
  31:     }
  32: }

So there you have it.  Add a simple policy file and you are enabled!  I hope this helps!  You can download a copy of this VS project here: XDomain.zip

Please enjoy some of these other recent posts...

Comments