Monthly Archives: May 2015

Implementing the OAUTH 2.0 flow in app for office sandboxed environment

EDIT: this approach is outdated. You can have a look at the sample we released and especially in the README section we compare the different approaches
https://github.com/Keluro/Office365-AddinWeb-SignInSample

In this post I will explain how I implemented the OAUTH 2.0 flow in the app for office sandboxed environment. The objective of this post is to discuss with others if the proposed implementation is satisfactory. Do not hesitate to criticize and propose alternatives.

Following the msdn documentation, the app for office model is the new model for

engaging new consumer and enterprise experiences for Office client applications. Using the power of the web and standard web technologies like HTML5, XML, CSS3, JavaScript, and REST APIs, you can create apps that interact with Office documents, email messages, meeting requests, and appointments.

Personally, I find this extremely promising, I even release my first app last year: Keluro web analytics that is distributed here on the store. I even wrote a small article dealing with app for office on the Keluro web blog.

Let us get straight to the point for this tech article. Apps for office aim at building connected service. For example, Keluro web analytics fetchs data from Google Analytics within MS Excel. I am working on a new project that must communicate with other Office 365 APIs. In all cases, you must use the OAUTH 2.0 flow to grant your app the right to communicate with those APIs. Sometimes there are some client libraries to leverage technicalities of the OAUTH flow. For my fist app, I used the Google Javascript API and for the second one I used the Azure Active Directory adal.js library.

However, there is problem. The apps for office run in a sandboxed environment that prevent cross domain navigation. This sandboxed environment is an iframe with sandbox attributes for the case of the office web apps. I asked the question for more information in this msdn thread. In all cases, the consequence is that the usual OAUTH 2.0 flow that redirects you to https://login.microsoft.com/etc. or https://apis.google.com to prompt the user for allowing access will not work. In addition, the pages served by those domains cannot be ‘iframed’ whether they are served to with X-Frame-Options deny or some js ‘frame busting’ is embedded.

I will get straight to the only working solution that I found. Actually, its the same recipe that I used for Google APIs and ADAL.

It looks like that the apps for office run with the following sandboxed attributes sandbox=”allow-scripts allow-forms allow-same-origin ms-allow-popups allow-popups”. Therefore, we are allowed to use popups and this popup is not restricted by CrossOrigin. What we will do is to open a popup and we will do the OAUTH flow in this popup, when its done we will get back the necessary information in the main app iframe. Here is a summary with a small graphic.

An overview of the solution found for the OAUTH2.0 in sandboxed env

An overview of the solution found for the OAUTH2.0 in sandboxed env

Then, you got it we will use a special callback.html file (that is basically blank and contains only javascript) as returned url. Here it its code.

<!--usual html5 doctype and meta-->
<body>
    <div id="oauthurl"></div>
     <script>
        var closeWindowCheck = function () {
        <!--transmit the hash of the return url to the parent iframe-->
            window.oauthHash =  location.hash;
            <!-- wait that the parent window give its approbation to close-->
            if (window.canclosewindow === true) { 
                window.close();
            }
        }
        var interval = setInterval(closeWindowCheck, 200);
    </script>
</body>
</html>

Then in the main iframe when it’s time to start the OAUTH flow, you have a piece of javascript that looks like the following

//this javascript lives in the sandboxed environment
//urlNavigate is the oauth2.0 url of the apis you are targeting with the parameters
//use callback.html as redirect uri.
var childWindow =window.open(urlNavigate,'Auth window', 'height=500,width=500');
var interval;
var pollChildWindow = function () {
    if (childWindow.closed === false) {
        var oauthHash;
        try {
           //this throws exception until the popup returns to
           //callback.html served in our domain.
            oauthHash = childWindow.oauthHash; 
        }catch(ex){}

        if (typeof oauthHash === &quot;string&quot;) {
            //childWindow.close(); // does not work with Chrome
            // ->; that is why we have the setInterval in callback.html javascript
            childWindow.canclosewindow = true; // allow the child window to close.
            var startWith = oauthHash.indexOf('#id_token');
            var currentRef = window.location.href.split('#')[0];
            //you get the id_token and other information from OAUTH2.0 
            //do whatever you have to do with it in your app.
            //YOUR LOGIC HERE
        }
    } else {
        clearInterval(interval);
    }
};                                                                                                                                          
interval = setInterval(pollChildWindow, 200);

I personally find this implementation not satisfactory because of the use of the popup (that may be blocked). What do you think of it ? Do you think of an alternative. I tried many things using postMessage API but it does not work with IE in sandboxed environment.

This is the implementation that is discussed in this ADAL.js issue. You may also find a setup of a sandboxed environment and some implementation attempt (without apps for office for simplicity) on my remote branch of my Adal.js fork.