Obtain an Office 365 OAuth token from within an Office Add-in without pop-ups or dialogs

In this article I will describe a simple process for generating and storing an O365 token from within an Office Add-in.

Introduction

In the previous article  I described the github project and sample code for creating and getting an Office 365 OAuth Token for use in an Office Add-in. This was an improvement on the previously accepted method for getting a token which required additional services and knowledge of C#. The biggest issue encountered was that the normal OAuth Token process when you log into Office365 takes you through multiple domains. When this happens in the Office Client Add-ins the user is thrown into a separate Internet Explorer. The generated token can then no longer be passed to the Add-in for programmatic use.

This article described how the issue has been overcome and the release of the new simplified code.

The same approach, but a new approach

As discussed in this article it is possible to access more than one domain within an Add-in by adding the additional domains to the Add-In manifest.

If we follow the OAuth process for Office 365 authorization we can see that there are two domains which are used:

We initially request access from “https://login.windows.net/common/oauth2/authorize? ” and then get redirected to “https://login.microsoftonline.com ” before getting sent back to the original.

To solve the issue at hand we add those two domains to the manifest in the following manner

  <AppDomains>
    <AppDomain>https://login.windows.net</AppDomain>
	<AppDomain>https://login.microsoftonline.com</AppDomain>
  </AppDomains>

In the github repo I have actually posted a complete sample manifest file for you to see.

The code 

d3

The code for the Authorization is relatively simple. The Home.js code is triggered when the page loads. If the location.href of the Add-In contains the access_token then we move to app.returnToken(). if not then we “getToken”.

//Home.js
(function () {
    'use strict';

    // The Office initialize function must be run each time a new page is loaded in the Add-In
    if (location.href.split("access_token").length < 2){
        Office.initialize = function (reason) {
            $(document).ready(function () {
                //check to see if there is an OAuth Token cached in the cookie
                //app.addinName will need to be different for every Add-In
                var token = app.getCookie(app.addinName)
                if (!token) {
                    var tokenParams = {};
                    tokenParams.authServer = 'https://login.windows.net/common/oauth2/authorize?';
                    tokenParams.responseType = 'token';
                    tokenParams.replyUrl = location.href.split("?")[0];

                    //THESE tokenParams need to be changed for your application
                    tokenParams.clientId = 'Your-app-clientId-goes-here';
                    tokenParams.resource = "https://yoursite.sharepoint.com";

                    app.getToken(tokenParams);
                } else {
                    //we have a token therefore carry on
                    app.tokenCallback(token)
                }
            });
        };
    } else {
        //The window has the access_token in the URL
        $(document).ready(function () {
            app.returnToken();
        });
    }
})();

The important parts of the App.js code are the getToken and returnToken. Within getToken(tokenParams) we parse out the incoming object and redirect the user to the login screen part of the process using location.href. The reason for having a Home.js separate from App.js is really for clarify.

//App.js
//...
    app.getToken = function (tokenParams) {

        var url =   tokenParams.authServer +
                    "response_type=" + encodeURI(tokenParams.responseType) + "&" +
                    "client_id=" + encodeURI(tokenParams.clientId) + "&" +
                    "resource=" + encodeURI(tokenParams.resource) + "&" +
                    "redirect_uri=" + encodeURI(tokenParams.replyUrl);

       // var winObj = window.open(url);
       // winObj.focus();
        location.href=url;
    };
//...

Putting this all together, when accessing the Add-in for the first time, the user now sees the login screens within the Add-in


o1

o2

And then once logged in the user is sent back to the original screen but with the Authorization token appended to the URL

o3

The returnToken() part of the application parses the incoming URL, extracts the Token and then inserts it into the page for display. The token is then stored in a cookie for 1 hour. This is the length of time the Authentication token is valid. The next time the user opens the Add-in within the hour the token will be stored and accessible.

//App.js
//...
app.returnToken = function(){
        var urlParameterExtraction = new (function () {
            function splitQueryString(queryStringFormattedString) {
                var split = queryStringFormattedString.split('&');

                // If there are no parameters in URL, do nothing.
                if (split == "") {
                    return {};
                }

                var results = {};

                // If there are parameters in URL, extract key/value pairs.
                for (var i = 0; i < split.length; ++i) {
                    var p = split[i].split('=', 2);
                    if (p.length == 1)
                        results[p[0]] = "";
                    else
                        results[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
                }
                return results;
            }

            // Split the query string (after removing preceding '#').
            this.queryStringParameters = splitQueryString(window.location.hash.substr(1));
        })();

        var token = urlParameterExtraction.queryStringParameters['access_token'];

        if (token){
            app.tokenCallback(token)
        } else {
            document.write("Shame")
        }
        /*if (window.opener){
            window.opener.app.tokenCallback(token);
            window.close()
        } else {
            document.write("There appears to have been an error, please close the window and check with your administrator")
        }*/

    }

	app.tokenCallback = function(token){
		//this would then continue to do what you really need in the Add-In
        document.getElementById('tokenHere').innerHTML = token
	}
//...

Sample code only

The code displayed in this example is for example only. It is there to demonstrate the process and return the token. What you then do with the token is up to you 🙂

Conclusion

In this article we have seen how we can create an Office 365 OAuth token with the minimum of impact on the user. They have to log into the Add-in within the Outlook client and the disruption on the User Experience is minimal.

 

Advertisement

6 thoughts on “Obtain an Office 365 OAuth token from within an Office Add-in without pop-ups or dialogs

  1. Hey there… nice article. It doesn’t seem to work for me though; I get the “‘X-Frame-Options’ to ‘deny'” for the login.microsoftonline.com url.

    Perhaps something’s changed with outlook?

    • That is the error I used to get before adding the AppDomains – maybe there is another URL your OAuth process is passing through which is triggering the issue. The best way to track that is through a tool like fiddler. Hope that helps.

      • Thanks for the reply! When I authorize the app for the first time, I am forced to use a pop-up for it to work fine. Once authorized, for further use of the app (across browser sessions), this code works fine. I’ll let you know if I find anything more.

  2. Hi!
    Very good article, I’m just trying to log in a website as authentication of the addin but I’m not very sure how can I do it. Do you have any article about that?

    Thank you

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s