Solving cross-domain problems in apps for SharePoint

Hello there, I'm Humberto Lezama-Guadarrama, Program Manager on the Office Developer Platform team. In this post I will analyze some of the challenges that developers will likely face when building apps for SharePoint and how our cross-domain library and APIs can help you solve them.

Cross-domain, the eternal challenge

Almost all web developers have at some point in their career faced cross-domain problems while building web apps. In a nutshell, you encounter these type of issues whenever an app wants to make client-side calls (for example, using JavaScript + XMLHttpRequest) from a page hosted in one domain (for example, https://www.fabrikampapp.com/appPage.html) to a page or service hosted in a different domain (for example, https://contoso.sharepoint.com). By default, browsers block this type of communication for security reasons; they don't want malicious apps to grab data or execute code without users knowing it.

So, what's the big deal? Well, the very nature of the new app model for SharePoint encourages developers to host code outside of SharePoint (see hosting options), which means that, by design, apps will face cross-domain challenges.  You can use server-side code + OAuth to avoid cross-domain issues (because the communication in that case is server-to-server), but what if you want to use JavaScript?  We all love JavaScript don't we? :)

Cross-domain library for apps for SharePoint

There are many different techniques to overcome cross-domain issues in JavaScript, several of which are pretty hacky. The most robust solutions to date, that work in all major browsers, involve using the IFrame postMessage method to establish mutual trust between pages loaded from different domains. Technically, somebody writing an app for SharePoint could solve cross-domain problems on their own, but we wanted to provide a robust, secure library that app developers can take advantage of.

So, how does it work? The gist is that we take advantage of a really cool feature in apps for SharePoint named app webs. Essentially, every time an app is installed (and contains at least one artifact that is deployed into SharePoint, such as a blank page) a dynamic endpoint is created on the fly. This dynamic endpoint (the app web) has its own domain, which means that any client-side calls to it are automatically security trimmed so the app cannot ever elevate its privileges (and cause security issues).  What the cross-domain library does is that it creates a hidden Iframe that wires your remote page with a proxy page on the app web. We then use the IFrame postMessage method to relay the calls on the client side. This effectively enables you to securely make calls to SharePoint using JavaScript. Figure 1 summarizes this process.

clip_image002

Figure 1. Cross-domain library execution process

 

Wait a minute, but how does SharePoint know how to "trust" the external domain (for example, fabrikamappp.com in the Figure 1)? The answer is in the type of hosting that you are doing; cross-domain calls are supported for all types of hosting:

  • For SharePoint-hosted apps, you declare which domain to "trust" by using an internal app principal; you do this in the AppManifest.xml. This sounds a bit counterintuitive right? Why would you use a SharePoint-hosted app if you are going to host pages outside of SharePoint?  Well, because you need the app web to communicate back to SharePoint. To create an app web you have to host "something" on SharePoint, even if it's just a default page. You could of course also host more interesting things, like document libraries or lists, and use your remote page to access them.
    <AppPrincipal> <Internal AllowedRemoteHostUrl="https://fabrikamapp.com/appPage.html" /> </AppPrincipal>
  • For provider-hosted apps, SharePoint trusts the domain that you register as part of your OAuth registration (even if you don't actually use OAuth). Provider-hosted apps built for the marketplace use the seller dashboard to register their domain, as shown in Figure 2. For more details consult the guidelines for registering apps for SharePoint 2013.

clip_image004

Figure 2. Seller dashboard’s domain registration page

  • For autohosted apps, you don't have to worry about it. We automatically take care of registering the dynamically provisioned Azure website so you can use our library without any additional registration.

Sounds a bit complex, huh?  Well, it kind of is under the hood. But, actually using the cross-domain library after registering your external domain is pretty easy. You just have to reference our JavaScript library and initialize it, and then you're done.  Here's a snippet:

 // Load the cross-domain library. 
$(document).ready(function () { 
  var hostweburl; 
  var appweburl; 
  
  //Get the URI decoded URLs. 
  hostweburl = decodeURIComponent( getQueryStringParameter("SPHostUrl") ); 
  appweburl = decodeURIComponent( getQueryStringParameter("SPAppWebUrl") ); 
  
  // Load the .js files using jQuery's getScript function. 
  $.getScript(
    hostweburl + "/_layouts/15/SP.RequestExecutor.js",
    continueExecution
  );
  
  // After the cross-domain library is loaded, execution 
  // continues to this function. 
  function continueExecution() { 
    var executor; 
    
    // Initialize your RequestExecutor object. 
    executor = new SP.RequestExecutor(appweburl); 
    // You can issue requests here using the executeAsync method 
    // of the RequestExecutor object.
  } 
  
  // Function to retrieve a query string value.  
  function getQueryStringParameter(paramToRetrieve) {
    var params = document.URL.split("?")[1].split("&");
    var strParams = "";

    for (var i = 0; i < params.length; i = i + 1) {
      var singleParam = params[i].split("=");
      if (singleParam[0] == paramToRetrieve)
        return singleParam[1];
    }
  }
});

  

We have a detailed how to article that explains the cross-domain library and several samples for you to try right away. You might be pretty surprised about how quickly you can put something together.

Important Note: You may also face some challenges with Internet Explorer security features that prevent cross-internet zones from sharing cookies. The way this manifests itself is that the cross-domain library fails to load and you always get an error.  Solving this problem is pretty straightforward—the details of the issue and the resolution are documented in this article.

Cross-site collection calls

A different problem that is often lumped into the same cross-domain category is issuing calls to SharePoint across sites or across site collections. For example, an app installed at https://contoso.sharepoint.com/site1 wants to retrieve a list from https://contoso.sharepoint.com/site2.

On the surface this looks like a cross-domain problem, but it is not. It's actually a traversal problem inside SharePoint resources.  What you really want is for your app to talk to your allowed endpoint (your app web) and from there you want to internally proxy the call to a different site collection.

So, does that mean that you are out of luck if you want to use JavaScript?  Nope, we actually have a pretty handy object for you named AppContextSite.  All you have to do is set AppContextSite to point to the target web you want to talk to. Please note that in order for cross-site collection calls to work your app must be deployed as a tenant scoped app by an administrator; this is currently a security restriction of the API. Here is the syntax:

JSOM

Syntax:

 var ctx = new SP.ClientContext(appWebUrl);
var appContextSite = new SP.AppContextSite(ctx, targetUrl);
ctx.Load(appContextSite.get_web());

Example:

 var ctx = new SP.ClientContext(
  "https://contoso-5334ef4b86c8ea.sharepointonline.com/app1"
); //Get this from tokens instead of hardcoding it. 
var appContextSite = new SP.AppContextSite(
  ctx, https://contoso.sharepointonline.com/anothersite
); //Get this from user input or context tokens. 
ctx.Load(appContextSite.get_web());

 

REST

Syntax:

appWebUrl/_api/SP.AppContextSite(@t)/web?@t='targetUrl'

Full URL Example:

https://contoso-5334ef4b86c8ea.sharepointonline.com/app1/_api/SP.AppContextSite(\@t)/web?\@t='https://contoso.sharepointonline.com/anothersite'

 

Wow, that is a lot of info right? In practice though, it is pretty straightforward after you learn how the pieces fit together. In the vast majority of cases all you have to do is reference our cross-domain library and use it and you are good to go. If you do encounter issues, we are monitoring our support forums so feel free to ping us.

Happy coding!