We have recently been implementing some Provider Hosted Add-Ins as Add-In Parts and had to face some pretty nasty issues. I want to describe some background around our findings and maybe help you to navigate around the obsicles we hit hardly using client side code such as Java Script and Angular in our Add-In.
Basic Structure of the Provider Hosted Add-In (formaly known as Provider Hosted App)
A Provider Hosted Add-In Part consists of multiple components. An Add-In Part that is placed on a SharePoint Page within a SharePoint Site, called the Host-Web. This Add-In Part renders an iFrame that contains the actual App hosted on the provider, for example an IIS-Server. The App could be an MVC application or a plain aspx-page hosting for example an AngularJS SPA (Single Page Application). Optionally the App can have an App-Web. This App-Web recides in SharePoint and can contain e.g. lists storing some data.
So far, so simple. To understand some issues we were facing you need to understand some basic concepts of where the three components are located. The Host-Web might have the URL http://myDomain.com. When SharePoint generates an App-Web it gets an URL with http://someAppWebGuid.myAppDomain.com. And at last the provider hosted App gets its own domain as well. For example the URL http://myProviderDomain.com. And these three domains now need to be connected to eachother. This is handled with some certificates installed in SharePoint and some keys and id’s to identify and trust the servers. I do not want to go into detail to deep here because the server side code using CSOM or REST calls work pretty well in this scenario. You can read more about setting up the high-trust using certificates here on MSDN*. The issues we were facing came up using client side calls in Java Script / Type Script. And this is where CORS (Cross-Origin Resources Sharing) and Kerberos come into play.
What does CORS have to do with SharePoint Add-Ins?
Now, if you have set up your provider hosted scenario and managed to get the exhausting parts with the certificates and trusts all right, you can start implementing your client side app. Within the Java Script you are now calling some SharePoint services using JSOM or REST. The following image shows the architecture of this scenario.
While testing we figured out that this works well within the Internet Explorer. But when using a browser like Google Chrome we faced the following error:
XMLHttpRequest cannot load https://myDomain.com/_api/someService. No ‚Access-Control-Allow-Origin‘ header is present on the requested resource. Origin ‚https://myProviderDomain‚ is therefore not allowed access. The response had HTTP status code 401.
This error is caused by a security „feature“ called CORS or Cross-Origin Resources Sharing. It is a recommendet standard of W3C that forbids cross domain AJAX calls. While you may embed images, fonts, css and other resources from other domains in your website it is a security issue when calling web services from other domains. You need to explicitly allow other domains to be called as a remote web service. In classic web applications this can be acieved by modifiying the .htaccess file (apache see here*) or the web.config (IIS see here*). But that does not work for SharePoint. SharePoint ignores the changes made within the IIS. So we need to find some other solution.
What does Kerberos have to do with SharePoint Add-Ins?
For most scenarios this solution perfectliy suits all needs. But now we add a little more complexity to our scenario. We want to use the provider hosted add-in in a mobile scenario. In our current scenario we have to use Mobile Iron to access the company’s intranet with the mobile device. Mobile Iron enforces Kerberos – which makes sense in terms of security. Considering the „dangerous“ internet I would not keep using NTLM as authentication method when opening my SharePoint to external mobile devices as well.
Now the fun part starts. Does SharePoint – and specifically do Add-Ins support Kerberos? Microsoft says „yes“ – with a little bit of „but“.
Yes, but only when it is configured to use NTLM as a fallback authentication method. Kerberos-only is not supported.*
Let’s add some mobile devices to have some more fun
In a mobile scenario this means that we have less security and we need to authenticate against SharePoint by typing in our username and password. And this every time a double hop or a different domain comes into play. For example if you have three Add-Ins added to your page you need to authenticate at least three times until the page is displayed.
Now you can try to enabe Kerberos anyway, as we did. You may figure out that this could work but brings a lot of overhead when maintaining your system. Each time you place an Add-In to your site an App-Web is is created. This App-Web gets an unique URL like http://someAppWebGuid.myAppDomain.com. As Kerberos does not support wildcarding (*) this URL must be registered as SPN (Service Principal Name). And this registration must be done by the Active Directory Team each time a new App-Web is created. Not only that this is a high manual effort the maximum number of possible SPNs is round about 1.200.
Thinking this through – after placing more than 1.200 Add-Ins Kerberos authentication does not work any more. You may split you apps to different domains and not open all your sites for mobile access but you only adjurn the problem to some day in the future where you will hit the limit hardly. And for what I know there currently is no solution to this circumstance.
The Solution to all our problems – an own Web Service
After all this trouble we finally found a solution which really isn’t awsome and fast and lean and everything, but it is the only one that worked in our szenario.
We implement our own REST Web Service on the provider server and execute the calls to SharePoint from the server side using CSOM. The servers trust each other and the web service is located in the same domain as the Add-In itself – so no CORS issues.
The following figure shows the architecture of that approach:
My colleague Stephan has created a blog post* about the concrete implementation of the seb service and it’s call from Java Script. Unfortunately it is in german but you might figure it out.
The downsides of that approach
Sounds good, doesn’t it? Nevertheless there are some downsides to this approach. But with some measures you can implement some mitigation for a few of them.
At first you need to take care about authentication. Meaning if you want a user’s context you need to get the authentication token from SharePoint through the header.
Secondly for CSOM to work you need to create a SharePoint Context on the Provider side to then connect to SharePoint to get your requested data.
The authentication and creation of the SharePoint Context take a long time in terms of „real time“, as well as the high amount of communication between the systems. In our development/test environment a web service call took up to two seconds to return the results. This is not acceptable in a highly frequented application. You can try to increase performance by creating bulk operations or caching the results. This can then increase performance dramatically but you might get some additional overhead on your solution. For us it worked and reduced to roundtrip times to a few milliseconds.
In my opinion the concept of SharePoint hosted Add-Ins and Provider Hosted Add-Ins is not really brought to a good end an we developers have to navigate around a lot of obsticles to accieve our goals that in theory always sound so simple. I hope this article can save you some time when creating your own Add-Ins.
If you have some better solutions than we have found I would be happy to hear about them. Let me know in the comments.
* Link refers to external page and opens in a new tab