11436 SSO

Crossing the Streams: Handling cross-site API requests (JSONP, CORS, UMP and more)

Gregory Brail
Mar 16, 2011

My earlier post "Not using JSON and JSONP? You're doing it wrong!"  generated a lot of questions about the best ways to handle cross-site API requests from JavaScript running in the browser.

(Short intro -- the browser won't let you do it with the standard XmlHttpRequest API because of the "same-origin policy." This policy is in your browser's JavaScript engine so when you hit an application at www.foo.com, there's no way for www.badguys.com to return bad data to your application.)

This is an important topic, because if you are providing an API at api.yourapi.com, no one can use your API from inside a browser unless you implement one, and preferably more than one, of these techniques. It's also important because the state of the art is changing and changing fast.

Here's a run-down of the various techniques today, and how you might choose,  with the inevitable links to Wikipedia to help guide you: 


What it is: You return JSON from your API, and wrap it with a user-supplied "callback" identifier. This turns your JSON response from a JavaScript object into a function call or an assignment statement. Your user uses the DOM API to dynamically insert "script" tags into the web page to cause your API to be invoked, then executes the result as a JavaScript function call to create the object and start using it. 

Why it's good: Works on every browser, and asynchronously like every JavaScript network resource to boot. If users use a library like jQuery it is very easy to use.

Why it's bad: Only works with JSON,  although if you read my last entry then your API returns JSON anyway! It only works with GET, so you have to pollute your beautiful and philosophically-correct REST API by adding something like an "action" query parameter -- this tells your server that even though it just got a "GET" call, it should behave as if it were a POST, and you can't upload data with GET.

Finally, JSONP is really a hack around the security sandbox in your browser. As a result, it lets your JavaScript code download executable code from  another domain and run it inside the browser. If the user of your API trusts you, then that's great, but otherwise they're placing the security of their web app in your hands.

Aside from offering JSON as a response format with callbacks, you can also specify XML as the response with JSONP-X.



What it is: Cross Origin Resource Sharing is a W3C spec that allows a web service to specify that it's OK for it to be invoked from any domain. The API provider implements it by returning a special set of HTTP headers with every API response, and also by implementing an HTTP OPTIONS "pre-flight" request that lets the browser check first to see if a request is going to be OK. The actual spec is awfully complex but what you actually need to know in most cases isn't all that bad.

Why it's good: Because it lets the API consumer easily say, "it's OK, browser -- go ahead and make that API call." Once it's implemented, browsers that understand the CORS spec will automatically open a hole in the cross-origin policy as part of the standard XmlHttpRequest API -- the programmer of the web app doesn't have to do anything special. Plus, it can return any kind of data, not just JSON, and it works with all HTTP verbs. 

Why it's bad: It's pretty new and not all browsers implement it. Firefox 3.5, Chrome 3, and Safari 4 has it. IE8 does too but using a slightly different API. And support in many smaller mobile browsers (like Opera) is sketchy or nonexistent.

In other words, CORS is super-duper awesome as long as you don't care about people who run IE -- whether that's important to you is up to you ;-)



What it is: UMP is a re-boot of CORS -- it handles 90 percent (or more) of the reasons an API would want to implement CORS without 90 percent of the complexity. It's also backward compatible in that it uses many of the same HTTP headers as CORS.

Why it's good: Like CORS, it allows an API to easily make itself available to web browser clients with a minimum of fuss. Better yet, all the API has to do is set a few HTTP headers, without also implementing an OPTIONS request.

Why it's bad: It's even newer than CORS. (New enough that it doesn't even have a Wikipedia page.) I have been told that recent versions of Chrome support it but it's not clear where else it can work at this point.

There's more to compare CORS and UMP here


Cross domain files

What they are: Flash and Silverlight also have a same-origin policy, but they do not work the same as the JavaScript engine and the techniques above do apply. What they do support are special files, namely crossdomain.xml (for Flash) and clientaccesspolicy.xml (for Silverlight). These function the same way as CORS and UMP headers in that they tell the client when it's OK to invoke the API across domains.

Why they're good: If you have Flash or Silverlight clients for your API, you need these too so that your API can be invoked from  those environments.

Why they're bad: If you don't want or need to support Flash and Silverlight clients then you don't need them.


So which one should I pick?

 If you want your API to be adopted in as many places as possible then you should implement all of them! Seriously:

1.       Always make it possible for your API to return JSON.

2.       Support JSONP as much as you can (since you can't use it to upload data that doesn't fit in the URL) and leave it up to the users of your API to assess the security risks

3.       Support CORS by returning the Access-Control-Allow-Origin header on all API responses and implementing the OPTIONS verb as specified in the CORS spec.

4.       If you do the above your API will also support UMP.

5.       Return a crossdomain.xml and clientaccesspolicy.xml file from the top level of your API domain for Flash and Silverlight clients.


At Apigee, we have done the above for users of our Apigee Enterprise product, and since it's so flexible we can change what we support as the standards change.

Scaling Microservices