Adding Auth0 to the Aurelia Tutorial

In my last post, I breezed my way through the Aurelia Tutorial and I now have a working Aurelia app. Next on my list is authentication. I want to include a Sign In button on the toolbar. When I click on The Sign In button, it authenticates me through the Auth0 system and returns me to the same spot but authenticated, plus the button changes to Sign Out. When I click on Sign Out, it removes the authentication token and changes the button back to a Sign In button.

Preparing for Auth0

Before I can get started, I need a couple of things. Firstly, let’s install Auth0:

jspm install github:auth0/lock

The Auth0 Lock library isn’t TypeScript friendly. It’s not even ES6 and Promise friendly. It’s definitely in the camp of old-school ES5. As a result, I’m going to have to roll my own .d.ts file in order to use it. Fortunately, I’m only using a small portion of the API. I’ve created a wwwroot/components/Auth0Lock.d.ts file with the following contents:

interface Auth0Lock {
    new (clientID: string, domain: string): Auth0Lock;
    parseHash(hash: any): any;
    show(options: any): void;
}

This allows me to create a new Auth0Lock class and then use the parseHash() method (which is used for parsing the response from the Auth0 system when it redirects back) and the show() method (which shows the UI). I may add to this in the future as there are methods in the class for decoding the profile (for example).

The Custom Element

Next, I need to create a custom element in Aurelia to handle the authenticator. I’m going to call this custom element “app-authenticator”. It’s located in the wwwroot/components directory (which is where I placed the Auth0Lock.d.ts file). Here is the app-authenticator.html file:

<template>
    <div id="app-authenticator" click.trigger="click()">
        <i id="app-authenticator-icon" class="${classList}"></i><br/>
        <span id="app-authenticator-title">${title}</span>
    </div>
</template>

I’m going to have to do some styling on this later on, so don’t expect the visual to be spot on first time. I’ve created two bindable properties – the classList will be something like “fa fa-sign-in” normally and the title will be Sign In (or whatever is under the icon). In addition, I’ve defined an action method “click()” that will be called when the icon is clicked.

The TypeScript file app-authenticator.ts is bigger, so I’ll split it up. First, let’s look at the top of the file:

///<reference path="Auth0Lock.d.ts" />

import {customElement, bindable} from "aurelia-framework";
import * as Auth0Lock from "auth0/lock";

Here I’m adding a reference to the Auth0Lock.d.ts file that I created earlier. I’m also bringing in the Auth0Lock library using a wild card. That’s because the library is not “ES6-friendly” yet. Hopefully, Auth0 will think about ES2015 compatibility now that it’s a standard. Let’s move on to the constructor:

@customElement("app-authenticator")
export class AppAuthenticator {
    @bindable classList = "fa fa-sign-in";
    @bindable title = "Sign In";

    private lock: Auth0Lock;

    constructor() {
        console.log("[AppAuthenticator] in constructor...");
        this.lock = new Auth0Lock("{{CLIENTID}}", "{{DOMAIN}}.auth0.com");

        // Store the JWT for later use if we are returning from an authentication - this
        let hash = this.lock.parseHash(window.location.hash);
        if (hash && hash.id_token) {
            console.log("[AppAuthenticator] storing hash id_token in localStorage");
            localStorage.setItem("auth_token", hash.id_token);
        }
        if (hash && hash.error) {
            alert("Authentication Error! " + hash.error + "\n" + hash.error_description);
        }

        // IF the auth token exists, then we are authenticated
        let auth_token = localStorage.getItem("auth_token");
        if (auth_token) {
            this.setSignOut();
        } else {
            this.setSignIn();
        }
    }

Replace the {{CLIENTID}} and {{DOMAIN}} with the variables from your Auth0 application settings. I’m going to pull these from a settings API call “later” so that they aren’t embedded in the application. However, this is good enough for now.

Let’s think about the lifecycle of Auth0. When the user first hits this application, the user will not be signed in (hence there will be no hash available and no auth_token either). The user clicks on the Sign In button and Auth0Lock does its thing. The page is then reloaded, but this time there is a JSON Web Token in the hash. This is the first redirect back. At this point, I store the JSON Web Token in the local storage area for later retrieval.

Since the JSON Web Token is now available, I can use it to see whether the user is signed in. If there is an auth_token, the user is logged in. If there is no auth_token, the user is logged out. The two methods setSignOut and setSignIn set up the icon and title appropriately.

Let’s move on to the click handler. This is called when the user clicks on the icon:

    click() {
        console.log("[AppAuthenticator] click ");
        let auth_token = localStorage.getItem("auth_token");
        if (auth_token) {
            // We clicked on the sign-out sign
            localStorage.removeItem("auth_token");
            this.setSignIn();
        } else {
            // We clicked on the sign-in sign
            this.lock.show({
                authParams: {
                    scope: "openid"
                }
            });
        }
    }

I use the presence of the auth_token in localStorage to decide what to do. If the auth_token is present (i.e. the user is logged in), I remove it and set the icon to say “sign in”. If the auth_token is not present (i.e. the user is logged out), I show the Auth0Lock UI.

Finally, I have a couple of routines for swapping the icon and title underneath the icon at the same time. Because the properties are bound to the template variables, I can just set them and they will be reflected in the UI.

    // Make the icon a sign-in icon
    setSignIn() {
        console.log("[AppAuthenticator] Setting Sign-in Status");
        this.classList = "fa fa-sign-in";
        this.title = "Sign In";
    }

    // Make the icon a sign-out icon
    setSignOut() {
        console.log("[AppAuthenticator] Setting Sign-out Status");
        this.classList = "fa fa-sign-out";
        this.title = "Sign Out";
    }

I’m not quite finished yet, as you will see. Don’t forget to update the redirect URL in your Auth0 settings. I set mine to http://localhost:XXXXX/. First, let’s run this project and see what we get. You will want to open up the Javascript console to see the effect:

blog-0710-1

This is actually quite a simple problem to understand. Aurelia uses the hash to store the route. When you click on the Flickr link, you will see http://localhost:XXXXX/#/flickr. As a result, when Auth0 redirects back to us (with a new hash), the routing is lost.

I want the button to return to exactly the same place as before. To do this, I’m going to have to store the location before I clicked on the Sign In button, then restore it again later on. Here is my new click() method (with the change):

    click() {
        console.log("[AppAuthenticator] click ");
        let auth_token = localStorage.getItem("auth_token");
        if (auth_token) {
            // We clicked on the sign-out sign
            localStorage.removeItem("auth_token");
            this.setSignIn();
        } else {
            // We clicked on the sign-in sign
            localStorage.setItem("auth_redirect", window.location.href);
            this.lock.show({
                authParams: {
                    scope: "openid"
                }
            });
        }
    }

I’ve also adjusted the storage of the JWT in the constructor as follows:

        let hash = this.lock.parseHash(window.location.hash);
        if (hash && hash.id_token) {
            console.log("[AppAuthenticator] storing hash id_token in localStorage");
            localStorage.setItem("auth_token", hash.id_token);
            window.location.href = localStorage.getItem("auth_redirect");
        }

I’m using a temporary localStorage object to store the redirect back. This object gets written when you click on Sign In and read when you come back from authenticating.

I am far from done on the authentication front. Firstly, I want to be able to control content within the Aurelia app. Secondly, I want to be able to authenticate the user to a WebAPI back end. Both of these elements will be covered in future articles. In the mean time, you can check out the code for the project so far on my GitHub Repository.