Authenticated Azure CORS Requests using Active Directory and adal.js

As is evident from my last post, I am currently working with the Windowz Azure cloud as a hosting platform for a single page application based on React.js.

One of the first issues that popped up (beyond deploying static code) was how to set up CORS from this static site to an Azure-hosted API. This is relatively easy, but involves the adal.js library which is scarciliy documented. Below is detailed what is necessary to get this to work properly.

Azure portal set up

The first step is to go the Azure Web Portal (the new one) and go into the settings for your Web API. Here go to the CORS panel and add your public domain and a localhost domain for testing purposes (You can set up more fine-grained rules for CORS-requests using C#).

The azure CORS control panel, screenshot.
Make sure your domain (and `localhost` for testing) is in this list.

Once that is done, you need to get the adal.js library from github. You want the non-angular adal.min.js. Download this and link it to your project using either import or a script tag depending on your setup.

You then need to set up Active Directory for you web app. The details of doing so are listed on this Microsoft Support page, I won’t go into the exact server-side setup of this here.

Set up the AuthenticationContext

Once that is done, you can hook up the JavaScript frontend using adal.js. First you need to create the AuthenticationContext, which is the global instance for the authenticated entitiy. This should always be initialized on page load.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Set up the ADAL instance, we will use the throughout the app
var adalInstance = new AuthenticationContext({
  instance: 'https://login.microsoftonline.com/',
  
  // The client ID of the app from the Azure Portal
  clientId: 'aabbccee-aabb-1122-3344-556677889900',
  
  // Where do we want to go after logging out
  postLogoutRedirectUri: window.location.origin, 
  
  endpoints: {
    // The domain of API (requsets are made TO)
    // And the same client id as above
    "https://YOURAPIDOMAIN.azurewebsites.net/": "aabbccee-aabb-1122-3344-556677889900" 
  }
})

To run on page load

When the page loads, you need to see if we have been redirected from the Active Directory login page, you do this be checking isCallback on the URL hash.

This should be done on every page load after initializing the AuthenticationContext above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// If this returns true, the user is logged in
function authenticateToken() {
    if (adalInstance.getCachedUser()) {
        // If we have a cached login, use it
        return true
    }

    if (adalInstance.isCallback(window.location.hash)) {
        // This happens after the AD login screen,
        // handleWindowCallback will use the hash to
        // complete the login
        adalInstance.handleWindowCallback()
        return true
    }

    // Not logged in
    return false
}

if (authenticateToken()) {
    // User is logged in
}
else {
    // Not logged in, go to 401 page?
    window.location.pathname = '/401.html'
}

You need to call authenticateToken once on page load to make the login process functional.

To run when you want to log in and out

To trigger the login page, or to logout from the service. Use the following two functions:

1
2
3
4
5
// Will go to the Active Directory login page, and redirect back on successful login
adalInstance.login()

// Destroy all cookies, and redirects back to `postLogoutRedirectUri` in the AuthenticationContext config above
adalInstance.logOut()

To make authenticated CORS requests to Azure

This is what we have been waiting for, making authenticated cross-site requests to the Azure-hosted API. In this example I’m using the new standard fetch API.

What you need to do is call getCachedToken on your AuthenticationContext, this will return a bearer token you can attach to the request under the Authorization header.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function azureApiRequest(method, resource, body) {
    var resourceUrl = apiUrl + resource;
    
    // Use the client ID of your app for this call,
    // same as in the configuration earlier
    var bearerToken = adal.getCachedToken('aabbccee-aabb-1122-3344-556677889900')
   
   var opts = {
        method: method,
        headers: {
            'Authorization': 'Bearer ' +  bearerToken
        }
    }

    // I'm using JSON for my API, you can change this to your
    // heart's desire
    if (body) {
        opts.body = JSON.stringify(body)
        opts.headers['Content-Type'] = 'application/json'
    }

    return fetch(resourceUrl, opts)
        .then(response => {
            return response.json()
        }).catch(function(error) {
            console.log("Network problem: " + error)
            // Inspect the error further to see what is actually wrong
        }
    )
}

azureApiRequest('GET', '/Info').then(
    appInfo => console.log("Received: " + JSON.stringify(appInfo))
)

Hope this will be of help to other people struggling with getting this to work. The quality of Microsoft’s documentation is overall very low in my opinion, while they do have a lot of documentation it is often outdated or unnecessarily detailed. I seldom feel the people who wrote the documentation actually used the API in question.

How to set up a static website on Azure

I struggled for way too long with figuring out how to set up a static website hosted in the Azure cloud. To save the reader a lot of time, I’ll detail the approach. First create a git deployment user in the Azure admin panel:

Azure git deployment setup, screenshot.
Enter your deployment git user credentials.

Then go to the Properties panel, and copy the Git deployment URL. Add this as a remote to git:

git add remote azure https://secretuser@mywebapp.scm.azurewebsites.net:443/mywebapp.git

Do not push yet, you need to set up what folder to use as a website root. You do this by creating a .deployment file in the repository root directory. All you need to do in this file is to specify what folder in the repository contains the static HTML files. I suggest using a www folder in the root directory of the repository. In that case your .deployment file should contain:

[config]
project = www

That is all you need. The www folder will now be served as the root for your website (these options are documented here). Add an index.html file in here, and commit both the .deployment file and the www directory. Then push to Azure with:

git push azure master

Going to https://mywebapp.azurewebsites.net/ will now show your index.html in your browser, and you’re done!

The Mobile Era Conference

I have the pleasure of being one of the organizers of a new conferance in Oslo this fall — the Mobile Era conferance — the focus of which is obvious: mobile.

The venue & date has now been decided, and it will be in Gamle Museet in Oslo on the 3-4th of November. The venue looks great, and the talks are flowing in.

I am obviously biased, but I would still urge you to take a look. Tickets are not available yet but we will soon start selling them. The first few will get a heavy discount, so stay tuned. If you are interested in speaking there is now also a Call for Papers active, and you are welcome to send your talk in and it will considered for inclusion.

Prevent iTunes and Photos from launching on media buttons

On my work computer, I fight a constant struggle against the standard apps in OSX that launch at the most inopportune times. The greatest offenders are iTunes, that hijack the media buttons (back, play/pause and forward) to launch itself and Photos, that likes to start up every time I hook in a new iOS device.

However, there is actually a simple, built-in way that will prevent this from ever happening. If you go into the /Applications/ folder and bring up the info sheet for the Application in question and select “No Access” for the groups everyone and system. You must first click the little lock in the lower right and enter your admin password to allow you to make changes. This will prevent those apps from ever launching again, they will only briefly appear in the Dock when prompted.

The group everyone has been assigned no access in OSX file info sheet, screenshot from OSX
No access for iTunes & Photos makes me a more productive person.

The only drawback of this method is that you can no longer use those apps. However, for me that has a dedicated work machine. This is a very reasonable sacrifice. I have no desire to open Photos on it and preventing me from doing so is only a benefit in my eyes.

SEO for Single Page Applications

Recently as I’ve been working on val.digital (in swedish), which is entirely an React app, and other single page applications. I’ve struggled with SEO. The main reason being Googlebot is simply not yet smart enough to realize how to render a SPA properly in all scenarios.

It is true, that it sometimes manages to wait enough for the main site to load, but many times, all you will get when donig “Render & Fetch” in Webmaster Tools will be a blank render showing the loading bar.

Googlebot only renders the loading screen, screenshot from Google Webmaster Tools
Look at the screenshot to the left, this is going to be a problem.

At first, I was hopeful that it would still index correctly. Occasionally it appears to work, Googlebot has picked up keywords that are only present in the React application, and sometimes Render & Fetch will do the correct thing. Google has clearly it has realized that there is more stuff on the page.

However, this is not universally true that it can render it. During my time continously developing the page. Many terms have in fact not been picked up by Googlebot. And the most popular keyword for the site according to Webmaster Tools is “loading”. That is not great.

To resolve the issues, my first approach was to put some static HTML containing the menu, with <a> tags linking to the different subpages inside the React div that would be replaced with the app on load time. The HTML was almost identical to what would be outputted later by React, and was a “poor-mans server-side rendering”. My hope was them being in the HTML directly would help Google to find the other pages and index them. As otherwise it appeared to be no links at all on the front page when it was first loaded.

This was in vain.

The next approach was creating more text-heavy pages. Because val.digital presents political polls, all the pages are almost identical in structure, with huge charts everywhere. These are tough for to index. To remedy this I created a FAQ page with lots of text for Google to gobble up. This was not a success either, even a month after I added it to the page Google had not indexed any of the text on it. And neither has any of the other pages on the site had been added to the overview (despite them being in the Sitemap).

Shows 14 pages submitted via sitemap, an only 1 being indexed, screenshot from Google Webmaster Tools
A sad sight for any webmaster.

Next step was to implement server-side rendering of React, meaning every page is pre-rendered with the complete HTML that the client-side React will render when it runs. And this works! I had some struggles with getting React to render to a static HTML file (and not on-demand server-side), but once I finally got it working it has been smooth, and now finally Googlebot picks up all the keywords I want it to.

A graph that shows 5 pages indexed on 15th of May, screenshot from Google Webmaster Tools
Finally it seems Google picks up more than the front page.

In conclusion, server-side rendering should be seen as mandatory for single page applications as of today’s date. Even if Google sometimes manages to render the page with all JS working, it is not 100 % reliable and it will fail to properly index your entire site if you rely on it.

Spotify is ditching the hamburger menu

Finally Spotify has rejoiced and removed the dreaded hamburger from their app. I never really understood that choice, since it adds extra steps to all navigation. Take a look on the difference first:

Old Spotify interface on iPhone, with hamburger menu. New Spotify interface on iPhone, with navigation bar.
Old and new interface of Spotify compared side-by-side.

To dive into why the navigation bar is a superior choice. First note that unless you are changing song within the current playlist. You most likely need to navigate to another view to find something else to listen to. With the hamburger menu, this meant you had to reach all the way up in the corner with your fingers (which, believe it or not is an ardous task). After that, in the old interface you were presented with only 4 options. Search, Browse, Radio and Your Music, as seen below.

The contents of the old hamburger menu of Spotify on iPhone.
The contents of the hamburger menu in the old version of the app.

If I wanted to Search I then need to tap Search as well, and only then do I get the keyboard input. In the new version, the switch only requires a single tap. The same is true for the other options, everything is one tap closer to use, not to mention more discoverable.

Overall, an excellent choice on Spotify’s part. And if you don’t have the new interface yet, it should roll out to people over the coming months.

Setting up Let's Encrypt on Nginx

Yesterday I setup HTTPS on this domain, and on val.digital. I was pleasently suprised by the ease of which this was acheived. Following the guide by Digital Ocean, it only took 30 minutes to set up the encryption for one domain, including auto-renewal through cron.

If your domain is not encrypted at the moment, I whole-heartedly recommend doing so through Let’s Encrypt. All you need to do is clone the git repository, run the letsencrypt-auto command and add some directives to your nginx config file for the site in question.

React ES6 change listeners simplified

Porting several React projects to ES6, the lack of autobind is a serious annoyance. Previously you could rely on the compiler to automatically call autoBind on your event listeners allowing the following code to work as you’d expect:

1
2
3
4
5
6
7
8
9
10
class MyComponent = React.createClass({
  componentDidMount() {
    // Autobind will make this work
    MyFluxStore.addListener(this.onChange);
  }
  onChange(state) {
    // 'this' is actually 'this'. No need for .bind
    this.setState(state);
  }
}

This is no longer the case when you have migrated to ES6, here we need to call bind ourselves to make sure we get the proper behaviour.

1
2
3
4
5
6
7
8
9
10
class MyComponent extends React.Component {
  componentDidMount() {
    // the event listener needs an explicit bind
    MyFluxStore.addListener(this.onChange.bind(this));
  }
  onChange(state) {
    // 'this' is what we expect
    this.setState(state);
  }
}

The problem comes once the component in unmounted. Because bind always returns a new function, simply calling MyFluxStore.removeListener will not work because this.onChange.bind(this) != this.onChange.bind(this). The event handler will remain on the store, leaking the listener and memory.

The solution here is to store away the event handler in the constructor as a member variable, and use that when binding / unbinding:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MyComponent extends React.Component {
  constructor() {
    super();

    this._onChange = this.onChange.bind(this);

    this.state = { ... };
  }
  
  componentDidMount() {
    // the event listener needs an explicit bind
    MyFluxStore.addListener(this.onChange);
  }
  
  componentWillUnmount() {
    // Will remove the correct listener
    MyFluxStore.removeListener(this._onChange);
  }

  onChange(state) { ... }
}

This leads to a lot of cruft code though. Every bind/unbind cycle needs to be listed thrice in every component. Which is a drag. To solve this, I made an extension to the BaseStore class from flux. This changes the addChangeListener and overrides EventEmitter.removeListener in order to allow you to simple pass the same arguments and it will find the listener you need to remove. Allowing you to rewrite the code above as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyComponent extends React.Component {
  constructor() {
    super();

    this.state = { ... };
  }
  
  componentDidMount() {
    // Pass the arguments you want to bind after the function, store will handle binding
    MyFluxStore.addListener(this.onChange, this);
  }
  
  componentWillUnmount() {
    // Pass same arguments like in componentDidMount and it will find the function and remove it
    MyFluxStore.removeListener(this.onChange, this);
  }

  onChange(state) { ... }
}

How does this magic work? It simple keeps a list in the store of what arguments were passed with a reference to the bound function. Then when you remove a listener it searches the internal list and remove that listener.

This carries no performance overhead when the listener is called, only a small loss when a listener is removed (because of the need to search for it in the list).

The code is available as a Github gist, feel free to comment there if you have remarks or comments.

Below is the excerpted addChangeListener function. As can be seen, it keeps a reference to the bound function by adding it to the global _listenerArguments map, and adds an internal tag to the listener. If you look at removeChangeListener in the gist above you will see it uses the bound listener to reverse the operation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  addChangeListener(listener) {
    if (typeof listener != 'function') {
      throw new TypeError("'listener' argument must be a function");
    }

    if (arguments.length == 1) {
      // Simple callback, just register it
      this.on(Constants.CHANGE_EVENT, listener);
    }
    else {
      // More complex, bind it, and keep a reference
      let extraArguments = Array.prototype.slice.call(arguments, 1);
      let boundListener = listener.bind.apply(listener, extraArguments);

      // Remember the arguments we bound with
      boundListener._listenerId = _listenerIds++;
      _listenerArguments[boundListener._listenerId] = extraArguments;

      // Register the listener
      this.on(Constants.CHANGE_EVENT, boundListener);
    }
  },

subscribe via RSS