Blog Archives

How to Avoid Chrome Security Issues Developing Office Add-in Hosted on localhost

When developing add-ins for Office you are often serving the add-in from a local web server on a URL using the host name “localhost”. Office add-ins also require the web server to use SSL to serve the resources for the add-in. The Chrome security implementation will fire off a security error under most common development scenarios. This is when the domain of the SSL certificate does not match “localhost”.

You will see this problem manifest itself by causing your add-in to not start and show an error stating:

“Add-in Error  Something went wrong and we couldn’t start this add-in. Please try again later or contact your system administrator”

cameron-dwyer-chrome-debug-localhost-00b-add-in-error-couldnt-start

If you have the Developer Tools window open in Chrome you will see error messages getting output with the text:

“net::ERR_INSECURE_RESPONSE”

cameron-dwyer-chrome-debug-localhost-00a-net-err-insecure-response

There is a relatively easy workaround to this problem that you can implement on your development machine to allow Chrome to bypass this certificate check on URLs served from “localhost”.

Type the following into the Chrome browser URL bar:

chrome://flags/#allow-insecure-localhost

Enable the option:

“Allow invalid certificates for resources loaded from localhost. Mac, Windows, Linux, Chrome OS, Android

Allows requests to localhost over HTTPS even when an invalid certificate is presented.”

cameron-dwyer-chrome-debug-localhost-01-allow-insecure-localhost

After making this change you will need to restart Chrome.

cameron-dwyer-chrome-debug-localhost-02-relaunch-now

Issues launching Outlook add-ins directly to a SPA route using fragment URLs

Modern Single Page Applications (SPAs) run in the browser as a single HTML page and use JavaScript to dynamically load content and provide the functionality of the application without having to reload the entire browser window as the user is interacting with the application. To support navigating SPAs, a commonly used technique is to perform navigation (or routing as it is called in the Angular world) based on URL fragments.

The basic premise of how this works is that the URL that you see in the browser window always refers to the same HTML page hosting your SPA e.g. http://myserver/spa-app/index.html

As the user navigates around the application this is done using URL fragments (the bit after the #) e.g. http://myserver/spa-app/index.html#configurationpage or http://myserver/spa-app/index.html#customerspage

This allows the browser to not go back to the server to request a page refresh (because we are always on the same page http://myserver/spa-app/index.html) but the SPA can react to the change of URL by reading the Fragment of the URL and route the user to the correct area of the app. The browser history also keeps track of the Fragment URL so this can provide a nice navigation experience.

That was a very basic explanation and I suggest reading this good primer on Fragment URLs (or hashbangs as they are sometimes referred).

So this leads us to Outlook add-ins and the problem I’ve encountered. Lets illustrate this with an example so that the use case becomes clear.

Imagine we have a simple SPA that shows a To Do list. The main screen of the app (http://myserver/spa-app/index.html) just shows the To Do list. There is also a second screen in the app for creating new To Do items(http://myserver/spa-app/index.html#newitem).

In the Outlook add-in manifest you provide a URL to your page that Outlook will load up in response to the user activating your app. The Microsoft preferred way of triggering this in Outlook is via Commands that appear as buttons in the Outlook Ribbon (in the desktop version of Outlook). If we create such a Ribbon button and specify the URL of the main screen of the app (http://myserver/spa-app/index.html) everything works just fine. Within the app itself, it can navigate off to http://myserver/spa-app/index.html#newitem to show the screen to create a new item. But what if we want to provide Outlook Ribbon buttons that streamline the process and let the user go straight to creating a new to do item rather than first having to open the app, then navigate within the app to create the item? Having the main functions of you app accessible as Ribbon buttons in Outlook is a huge time saver for users.

So what happens when we try to use the new item URL Fragment behind a Ribbon button?

If we specify a URL of http://myserver/spa-app/index.html#newitem in the add-in manifest, the following is the URL that Outlook actually launches the add-in with:

http://myserver/spa-app/index.html?et=&_host_Info=Outlook|Web|1…_1480636166782|ocii1|https://outlook.office.com/owa/?realm=XYZ.com#&_serializer_version=1newitem&_xdm_Info=-133b2041_-3d735892_1480636166782|ocii1|https://outlook.office.com/owa/?realm=XYZ.com

 

Obviously this is going to wreak havoc with your SPA. The original URL Fragment #newitem looks to be encoded in the resulting URL as “&_serializer_version=1newitem” although how to reliably detect and extract this and then do the correct routing within your SPA is challenging!

Getting the Angular 2 Router Working for an Office Addin

 

I have simple Angular 2 Office Addin and attempting to use the Angular 2 Router to route between two components. My two components are called ViewOne and ViewTwo.

Here’s what the UI for the Office Addin looks like:

office-addin-angular2-router-pushState-cameron-dwyer-01-addin-ui

When the using the Router to navigate, the following errors related to the this._history.pushState function are thrown to the JavaScript console

office-addin-angular2-router-pushState-cameron-dwyer-02-zone-js-error

The error message text is:

EXCEPTION: Error: Uncaught (in promise): TypeError: this_history.pushState is not a function

The same page displays without any error if it is not running as an Office Addin (rather if I just run the same router code on a standalone web page).

My best guess is that this error is due to the Office Addin framework and the fact that the Angular 2 app is running inside a sandbox iframe. I have tried running the same Angular app in a sandbox iframe on an otherwise generic html page however and I can’t reproduce the error so I think it is unique to something within the Office Addin framework.

This particular error has to do with the Angular 2 app trying to push the URL change to the web browsers history (to support back/forward navigation). In an Office Addin this doesn’t really make much sense as the Addin isn’t in control of the whole page so we wouldn’t want the Addin taking over the browsers URL history anyway.

In order to stop the Angular 2 router trying to make this call to the browser you can use a custom location strategy. In my case I was already using the HashLocationStrategy (rather than the default HTML5 routing strategy).

I went to the Angular 2 GitHub repo and found the source code for the HashLocationStrategy and created a new class in my Angular 2 app called CustomHashLocationStrategy. I just dumped all the source code into the new file, changed the name of the class and removed the two lines of code that try to update the web browsers history as shown below.

office-addin-angular2-router-pushState-cameron-dwyer-03-location-strategy

Now when bootstrapping my Angular 2 app I use my new CustomHashLocationStrategy instead of the HashLocationStrategy. Here’s what that change looks like in code.

Before (click for full size image):

office-addin-angular2-router-pushState-cameron-dwyer-04-bootstrap-before

After (click for full size image):

office-addin-angular2-router-pushState-cameron-dwyer-04b-bootstrap-after

After this change I can now navigate between the 2 routes without any errors being thrown to the console.

office-addin-angular2-router-pushState-cameron-dwyer-05-addin-ui-working

The code shown in this article in the Angular 2 Router in RC1. I also had the same issue using the “Router-Deprecated” in RC1, the same solutions worked for me using the deprecated router.

I also tested that this fix worked across Chrome, IE, Edge and Windows Desktop Office Client.

 

Further reading:

http://stackoverflow.com/questions/36182807/angular-2-routed-apps-hosted-in-iframes

https://www.illucit.com/blog/2016/05/angular2-release-candidate-1-rc1-changes/

https://angular.io/docs/ts/latest/guide/router.html

%d bloggers like this: