Here’s the final post in this 3 part Silverlight HTTP networking series.
Today, we’ll drill in to how to configure your web service to enable Silverlight cross domain callers. If you haven’t already, please be sure to read (at least) part 2 of this series – it provides a lot context for this discussion.
Note: this tutorial has been updated for Silverlight 2 RTW
So, You Want To Make Your Web Service Callable By 3rd Parties…
Exposing your web service to 3rd parties is an important decision that should be done with care and diligence. As you’re designing your web service APIs, please keep the following in mind:
1. Using cookies/auth on your main site? Create a separate domain for your 3rd party-accessible APIs
In Silverlight, we send cookies & authentication with each cross domain request. Because of this, web service authors need to be careful to separate their 3rd party accessible APIs from their main site.
Let’s look at example. http://cool.com uses cookie authentication. Once a user is logged on, she can access and update her public profile as well as her personal account information.
Now, http://cool.com wants to enable 3rd party apps to access public profile information. However, they don’t want to these 3rd party apps to get at a user’s billing information.
If the primary site and the web service is hosted on the one domain, then once a user logs in, cookies & auth are sent from http://cool.com applications well as 3rd party applications.
This could allow 3rd party sites to access protected information on the user’s account.
Instead, the web service apis should be hosted on a separate domain.
This way, even though cookies and auth are always sent, 3rd party applications cannot call (authenticated) web services on the main site.
Do you want to expose your web service to more than one partner, or to the public at large? If so, then basic/digest authentication and/or cookies aren’t sufficient for that web service authentication. Which leads us to…
2. Requiring 3rd party apps to authenticate? Use an in-message authentication approach.
You should not rely on cookies or basic/digest authentication to authenticate a request on a 3rd party exposed API. This is because multiple 3rd parties will be using the same APIs, and the same cookies and/or auth will be sent to http://api.cool.com regardless of the specific caller.
Let’s say a user trusts http://bar.com and authenticates to http://api.cool.com from http://bar.com/app.xap. That same user does NOT trust http://foo.com.
However, since http://api.cool.com uses cookie authentication, once the user has signed in, she is signed in for all calls from that browser session, not just a particular site. This means http://foo.com/app.xap can access private information, even though that wasn’t the user’s intent.
If you want to authenticate the particular application calling a web service method, it is better to use an in message authentication. For instance, you could specify the particular application’s key as a parameter in the query string.
With an in-message approach to authentication, you can determine which 3rd party application is calling your web service.
3. Set the cache policy on your cross domain policy file
As explained last time, we use the browser plugin networking APIs to issue requests for the cross domain policy file. This means that the normal browser rules around request caching apply to the policy file.
We recommend that you do NOT allow the browser to cache the policy file – this makes changing the policy file at later time much easier. Turning off client caching is done by configuring your server to set a “Cache-Control:no-cache” response header on the policy file.
Remember, we only check a site’s cross domain policy once per application session, so the bandwidth/latency cost of not caching the policy file is limited.
4. Restrict the policy as much as possible
The last piece of advice is the easiest, but the one most prone to copy & paste mistake: don’t open up a web service to everyone unless you *need* everyone to be able to call it.
If you’re just trying to enable certain particular partner apps to call your web service, allow access to that handful of domains.
Similarly, if you only really want public callers on a certain path, then just open up that subpath.
That being said, don’t configure a policy with 400 different <resource> tags – as that is also difficult to audit and maintain. As always, good security is a balance between reducing surface area and maintaining simplicity.
Configuring a Silverlight Policy File
The Silverlight policy file is called clientaccesspolicy.xml. A Silverilght policy file that opens up an entire domain to the public & allows Content-Type header to be sent looks like:
Specifying WHO is allowed by a policy
Within a policy, you specify allowed domains by:
- a specific domain (e.g. ‘http://bar.com’, ‘https://bar.com’)
- Represents only the domain with that specific protocol, host name & port.
- subdomain wildcard (e.g. ‘http://*.bar.com’)
- Represents all subdomains
- http wildcard (i.e. ‘http://*’)
- Represents all http:// domains
- general wildcard (i.e. ‘*’)
- Represents all domains (http:// & https://), if an http service
- Represents all secure domains (https://), if an https service
Note: If your service is an HTTPS service, you need to explicitly allow insecure HTTP callers with the ‘http://*’ literal. Otherwise, allowing ‘*” will only grant access to secure HTTPS callers.
Specifying WHAT is allowed by a policy
Also, within a policy, you specify the granted resources on the server by either:
- Supplying a path and setting include-subpaths to be false. This means that only requests that exactly match the specified path are allowed.
- Supplying a path and setting include-subpaths to be true. This mean requests whose paths are prefixed by the specified path are allowed.
Specifying HOW a request can be sent
By default, no request headers can be sent. You can designate which request headers should be allowed on POSTs by:
- “*” wildcard representing all non-blacklisted headers
- a comma seperated list of headers (e.g. “SOAPAction, Content-Type”)
It’s also possible to specify more than one policy within a policy file.
Here are some more clientaccesspolicy.xml examples:
The above policy allows requests coming from Silverlight applications on:
- http://sub.cool.com, http://partner.com, and http://friend.com.
They can send on POSTs:
- SOAPAction request headers
- Content-Type request headers. (Is allowed by default.)
They have access to:
- http://cool.com/shipments and all its subpaths (e.g. http://cool.com/shipments/, http://cool.com/shipments/details.xml, etc).
Note: The allowed domains do NOT have access to http://cool.com/creditcards/ or http://cool.com/creditcards/numbers.xml, as the “/creditcards” resource tag does not have an include-subpath=”true” attribute.
The above policy file contains two policies. Requests are allowed if…
- they come from an app on http://sub.com.com and are requesting http://cool.com/partners/feeds/favorites.rss
- OR they are for any subpath of http://cool.com/api
Configuring a Silverlight-Supported Flash Policy File
The Flash policy file is called crossdomain.xml. Silverlight supports crossdomain.xml files that allow public access to an entire domain. Specifically, this mean supporting crossdomain.xml of the format:
So, that’s our 3 part HTTP networking series. Hope you found it useful. Like always, we love to hear your feedback.
Can’t wait to see the applications you’re going to build!