Aurelia

Sample Project

Download this sample project configured with your Auth0 API Keys.

System Requirements

This tutorial and seed project have been tested with the following:

  • NodeJS 5.0.0
  • JSPM 0.16.27
  • Aurelia-framework 1.0.0-beta.1.1.0

Before Starting

Please remember that for security purposes, you have to register the URL of your app in the Application Settings section as the callbackURL.

Right now, that callback is set to the following:

https://YOUR_APP/callback

1. Add the Auth0 Scripts

Add Lock to your index.html file and set the viewport.

<!-- Auth0 Lock script -->
<script src="https://cdn.auth0.com/js/lock/10.6/lock.min.js"></script>

<!-- Setting the right viewport -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

2. Import Dependencies and Set Up Auth0Lock

Later we'll see how to make authenticated HTTP requests, and for that we'll need HttpClient from aurelia-fetch-client. We also need to create a new instance of Auth0Lock.

// app.js

import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-fetch-client';

@inject(HttpClient)

export class App {
  lock = new Auth0Lock('YOUR_CLIENT_ID', 'YOUR_AUTH0_DOMAIN');
  isAuthenticated = false;
  
  constructor(http) {
    this.http = http;
    var self = this;
    this.lock.on("authenticated", (authResult) => {
      self.lock.getProfile(authResult.idToken, (error, profile) => {
        if (error) {
          // Handle error
          return;
        }

        localStorage.setItem('id_token', authResult.idToken);
        localStorage.setItem('profile', JSON.stringify(profile));
        self.isAuthenticated = true;
        self.lock.hide();
      });
    });
  }
}

We also set the isAuthenticated property to false to start with, but this value will be changed later on to reflect the user's authentication status.

2. Set Up the Login and Logout Methods

The login and logout methods will be bound to button clicks in the template.

We first need to set up the buttons in our template. At the same time, we'll create a button that will be used for authenticated HTTP requests.

<!-- app.html -->
<template>
  <button click.delegate="login()" if.bind="!isAuthenticate">Log In</button>
  <button click.delegate="logout()" if.bind="isAuthenticated">Log Out</button>
  <hr>
  <button click.delegate="getSecretThing()" if.bind="isAuthenticated">Get Secret Thing</button>
  <h3>${secretThing}</h3>
</template>

The login method will show the Lock widget and save the user's profile and JWT in local storage if authentication is successful and set isAuthenticated to true.

// app.js

login() {
  this.lock.show();   
}

To log the user out, we just need to remove the their JWT and profile from local storage, and then set isAuthenticated to false.

// app.js

logout() {
  localStorage.removeItem('profile');
  localStorage.removeItem('id_token');
  this.isAuthenticated = false;   
}

Note: There are multiple ways of implementing login. The example above displays the Lock Widget. However you may implement your own login UI by changing the line <script src="https://cdn.auth0.com/js/lock/10.6/lock.min.js"></script> to <script src="https://cdn.auth0.com/w2/auth0-7.4.min.js"></script>.

3. Make Secure Calls to an API

To make secure calls to an API, attach the user's JWT as an Authorization header to the HTTP request. This is done in the RequestInit object as the second argument to the fetch call.

// app.js

getSecretThing() {
  this.http.fetch('/api/protected-route', {
    headers: {
      'Authorization': 'Bearer ' + localStorage.getItem('id_token')
    }
  })
  .then(response => response.json())
  .then(data => this.secretThing = data.text);
}

4. Configure All HTTP Calls to be Secure

If you wish to attach the user's JWT as an Authorization header on all HTTP calls, you can configure the HttpClient to do so.

// app.js

constructor(http) {
  this.http = http;
  this.http.configure(config => {
    config.withDefaults({
      headers: {
        'Authorization': 'Bearer ' + localStorage.getItem('id_token')
      }
    });
  });
  ...
}

You can then remove the RequestInit object (the second argument of fetch) from individual HTTP calls.

5. Optional: Decode the User's JWT to Check Expiry

Checking whether the user's JWT has expired is useful for conditionally showing or hiding elements and limiting access to certain routes. This can be done with the jwt-decode package and a simple function. First, install the package.

npm install jwt-decode --save

Next, we can create a utilities file that will have a function that decodes the JWT and checks whether it has expired.

// utils/tokenUtils.js

export function tokenIsExpired() {
  let jwt = localStorage.getItem('id_token')
  if(jwt) {
    let jwtExp = jwt_decode(jwt).exp;
    let expiryDate = new Date(0);
    expiryDate.setUTCSeconds(jwtExp);
    
    if(new Date() < expiryDate) {
      return false;
    }
  }

  return true;
}

With this method in place, we can now call it in the constructor so that the user's authentication state is preserved if the page is refreshed.

// app.js

import {tokenIsExpired} from './utils/tokenUtils';

constructor(http) {
  this.http = http;
  
  ...
  
  if(tokenIsExpired())  {
    this.isAuthenticated = false;
  }
  else {
    this.isAuthenticated = true;
  }
}

6. Check Whether a Route Can Be Activated

Aurelia's canActivate method can be used to check whether a route can be navigated to. If the user's JWT has expired, we don't want them to be able to navigate to private routes.

// private-route.js

import {Redirect} from 'aurelia-router';
import {tokenIsExpired} from './utils/tokenUtils';

export class Private {
  message = "Hello from a private route.";
  
  canActivate() {
    if(tokenIsExpired()) {
      return new Redirect('public');
    }
    else {
      return true;
    }
  }
}

This hook will redirect the user to some other route (public in this case) if the user's JWT has expired.

Persisting Application State

Lock uses redirect mode by default, meaning that a full page refresh will occur when users go through the authentication process in your application. This can be undesirable in single page apps, especially if the application contains state that should be displayed to the user again after they authenticate. For example, it may be the case that your users can initiate authentication from an arbitrary route within your single page app and you may want to return them to that location (along with any relevant state) after authentication is complete.

The recommended solution for this is to persist any necessary application state in local storage or a cookie before the user logs in or signs up. With this data, you can provide some custom post-authentication logic in the listener for the authenticated event to allow the user to pick up from where they left off.

In cases where storing application state is not possible, Lock's popup mode can be used. Please note that popup mode is not recommended. Known bugs in certain browsers prevent popup mode from functioning as expected under some circumstances.

Use Auth0 for FREECreate free Account