JavaScript
Article
By Ado Kukic

Easy AngularJS Authentication with Auth0

By Ado Kukic

This article was updated on 11.05.2017 to reflect important changes to Auth0’s API.

Authentication for single page apps can be a tricky matter. In many cases, SPA architecture involves having an isolated front-end application with a framework like AngularJS, and a separate backend that serves as a data API to feed the front-end. In these cases, traditional session-based authentication that is done in most round-trip applications falls short. Session-based authentication has a lot of issues for this kind of architecture, but probably the biggest is that it introduces state to the API, and one of the tenets of REST is that things remains stateless. Another consideration is that if you ever want to use that same data API as a backend for a mobile application, session-based authentication won’t work.

JSON Web Tokens

To get around these limitations, we can use JSON Web Tokens (JWT) to add authentication to our single page apps. JWT is an open standard and provides us a way to authenticate requests from our front end AngularJS app to our backend API. JWTs are more than just a token though. One of the biggest advantages of JWTs is that they include a data payload that can have arbitrary JSON data in the form of claims that we define. Since JWTs are digitally signed with a secret that lives on the server, we can rest assured that they can’t be tampered with and the data in the payload can’t be changed before reaching the backend.

Authentication for Angular Apps

JWTs are a perfect solution for adding authentication to our AngularJS apps. All we need to do to access secured endpoints from our API is save the user’s JWT in local storage and then send it as an Authorization header when we make HTTP requests. If the user has an invalid JWT or no JWT at all, their request to access the protected resoures will be denied, and they will get an error.

Unfortunately, this would be just the bare minimum for handling authentication in AngularJS apps. If we care at all about user experience, there are a few other things we need to do to make sure our apps behave as one would expect. We need to:

  • Conditionally show or hide certain elements depending on whether the user has a valid JWT (e.g.: Login and Logout buttons)
  • Protect certain routes that an unauthenticated user shouldn’t be able to access
  • Update the user interface when user state changes if their JWT expires or when they log out

In this article, we’ll implement authentication from start to finish in an AngularJS app, and we’ll even create a small NodeJS server to see how to make requets to a protected resource. There are a lot of details around setting up a user database and issuing JWTs, so instead of doing it on our own, we’ll use Auth0 (the company I work for) to do it for us. Auth0 provides a free plan for up to 7,000 active users, which gives us plenty of room in many real world applications. We’ll also see how we can easily add a login box and even use social authentication with Auth0.

Before we start, if you’d like a refresher on AngularJS, check out Building an App with AngularJS over on SitePoint Premium.

To see all the code for this tutorial, check out the repo.

angular authentication auth0

Sign up for Auth0

The first thing you’ll need for this tutorial is an Auth0 account. When signing up for an account, you will need to give your app a domain name which cannot be changed later. Since you can have multiple apps under the same account, how you name your domain will depend on your situation. In most cases, it’s best to name it with something that is relevant to your organization, such as your company’s name. If it makes sense, you can also use your application’s name–it’s up to you. Your Auth0 domain takes the pattern your-domain.auth0.com and is used when configuring the Auth0 tools that we’ll see below.

Once you’ve signed up, you’ll be asked what kind of authentication you’d like for your application. It’s fine to leave the defaults in place, as you’ll be able to change them later.

After you have signed up, head over to your dashboard to check things out. If you click the Clients link in the left sidebar, you’ll see that your account gets created with a Default App. Click the Default App to see your credentials and other details.

Angular authentication auth0

Right off the bat we should fill in our Allowed Origins and Allowed Callback URLs. This field is used to tell Auth0 which domains are allowed to make requests to authenticate users, as well as which domains we can redirect to after authentication has taken place. We’ll be using http-sever in this tutorial, which has a default origin of http://localhost:8080.

Next, since we are building a Single Page App that will talk to an API backend, let’s build an API client as well. Click on the APIs link in the main menu. From here, click the Create API button and you will be presented with a dialog that will ask you to fill in some information about your API. All you’ll need to provide is a Name and Identifier. Make note of the Identifier as this is the value that will be used as your audience identifier for the API. Leave the Signing Algorithm as RS256.

Create API client

With Auth0’s free plan, we are able to use two social identity providers, such as Google, Twitter, Facebook and many others. All we need to do to make them work is flip a switch and this can be done in the Connections > Social link in the dashboard.

Install the Dependencies and Configure Auth0

We’ll need a number of packages for this app, some of which are provided by Auth0 as open source modules. If you have forked the GitHub repo, you can simply run bower install to install all the needed dependencies. Once the dependencies have been installed, you will want to install the http-server module globally. To do so enter the following command:

# To serve the app (if not already installed)
npm install -g http-server

Finally, to get the app up and running, simply execute the http-server command from your terminal or command line interface.

Next, let’s set up our app.js and index.html files to bootstrap the application. At this time we can let Angular know which modules we need access to from the dependencies we installed.

// app.js

(function () {

  'use strict';

  angular
    .module('app', ['auth0.auth0', 'angular-jwt', 'ui.router'])
    .config(config);

  config.$inject = ['$stateProvider', '$locationProvider', 'angularAuth0Provider', '$urlRouterProvider', 'jwtOptionsProvider'];

  function config($stateProvider, $locationProvider, angularAuth0Provider, $urlRouterProvider, jwtOptionsProvider) {

    $stateProvider
      .state('home', {
        url: '/home',
        controller: 'HomeController',
        templateUrl: 'components/home/home.html',
        controllerAs: 'vm'
      })

    // Initialization for the angular-auth0 library
    angularAuth0Provider.init({
      clientID: AUTH0_CLIENT_ID, // Your Default Client ID
      domain: AUTH0_DOMAIN, // Your Auth0 Domain
      responseType: 'token id_token',
      redirectUri: AUTH0_CALLBACK_URL, // Your Callback URL
      audience: AUTH0_API_AUDIENCE, // The API Identifier value you gave your API
    });

    // Configure a tokenGetter so that the isAuthenticated
    // method from angular-jwt can be used
    jwtOptionsProvider.config({
      tokenGetter: function() {
        return localStorage.getItem('id_token');
      }
    });

    $urlRouterProvider.otherwise('/home');

    // Remove the ! from the hash so that
    // auth0.js can properly parse it
    $locationProvider.hashPrefix('');

  }

})();

Here we’ve configured authProvider from auth0-angular with our credentials from the dashboard. Of course, you’ll want to replace the values in the sample with your own credentials. Let’s also create an app.run.js file and paste the following code:

// app.run.js
(function () {

  'use strict';

  angular
    .module('app')
    .run(function ($rootScope, authService) {

      // Put the authService on $rootScope so its methods
      // can be accessed from the nav bar
      $rootScope.auth = authService;

      // Process the auth token if it exists and fetch the profile
      authService.handleParseHash();
    });

})();

What this piece of functionality will do is parse the hash to extract the access_token and id_token returned with the callback once a user has successfully authenticated. In a real-world application you may have a specific route to handle this such as /callback but for our simple demo will just have this run anytime the app is refreshed.

The access_token will be sent to your backend API and this token will be validated to ensure proper access. The id_token on the other hand is for the frontend client and holds user data for the client.

<!-- index.html -->
<html>
  <head>
    <title>AngularJS Authentication</title>

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

    <!-- Basic style -->
    <link href="bower_components/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
      <style>
      .navbar{
        margin-bottom: 0;
        border-radius: 0;
      }
    </style>
  </head>
  <body>
    <div ng-app="app">
      <nav class="navbar navbar-default">
        <div class="container-fluid">
          <div class="navbar-header">
            <a class="navbar-brand" ui-sref="home">Auth0</a>
          </div>
          <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
              <li ng-if="!auth.isAuthenticated()"><a ng-click="auth.login()">Log In</a></li>
              <li ng-if="auth.isAuthenticated()"><a ng-click="auth.logout()">Log Out</a></li>
            </ul>
          </div>
        </div>
      </nav>

      <div ui-view></div>
    </div>

    <script type="text/javascript" src="auth0-variables.js"></script>
    <script type="text/javascript" src="bower_components/angular/angular.js"></script>
    <script type="text/javascript" src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
    <script type="text/javascript" src="bower_components/auth0.js/build/auth0.js"></script>
    <script type="text/javascript" src="bower_components/angular-auth0/dist/angular-auth0.js"></script>
    <script type="text/javascript" src="bower_components/angular-jwt/dist/angular-jwt.js"></script>
    <script type="text/javascript" src="app.js"></script>
    <script type="text/javascript" src="app.run.js"></script>
    <script type="text/javascript" src="components/home/home.controller.js"></script>
    <script type="text/javascript" src="components/auth/auth.service.js"></script>
  </body>
</html>

Now we have the groundwork for our application setup. We have a simple toolbar at the top of the page that will allow the user to login. You’ll notice a bunch of imports at the bottom that we haven’t created yet. We’ll start building those out in the next section.

--ADVERTISEMENT--

Create the Homepage

There are several places in an app we could put controls for authentication. We could use a sidenav, a navbar, a modal, or even a mix of these three. To keep things simple, we already put a login button in the toolbar, but for a better user experience let’s also add it to our main view. If we look at our app.js file we’ll see that our home component will live in the components/home directory, so create this directory next with a home.controller.js file and a home.html file for the UI. Our UI will look like this:

 <!-- home.html -->
<div class="jumbotron">
  <h2 class="text-center"><img src="https://cdn.auth0.com/styleguide/1.0.0/img/badge.svg"></h2>
  <h2 class="text-center">Home</h2>
  <div class="text-center" ng-if="!vm.auth.isAuthenticated()">
    <p>You are not yet authenticated. <a href="javascript:;" ng-click="vm.auth.login()">Log in.</a></p>
  </div>
  <div class="text-center" ng-if="vm.auth.isAuthenticated()">
    <p>Thank you for logging in! <a href="javascript:;" ng-click="vm.auth.logout()">Log out.</a></p>
  </div>
  <div class="text-center">
    <a ng-click="vm.getMessage()">Get Message</a> <span style="padding: 0 50px;">|</span>
    <a ng-click="vm.getSecretMessage()">Get Secret Message</a>
    <br />
    <p>{{vm.message}}</p>
  </div>
</div>

For our home.controller.js file we’ll have the following code:

 // home.controller.js
(function () {

  'use strict';

  angular
    .module('app')
    .controller('HomeController', homeController);

  homeController.$inject = ['authService', '$http'];

  function homeController(authService, $http) {

    var vm = this;
    vm.auth = authService;

    vm.getMessage = function() {
      $http.get('http://localhost:3001/api/public').then(function(response) {
        vm.message = response.data.message;
      });
    }

    // Makes a call to a private endpoint.
    // We will append our access_token to the call and the backend will
    // verify that it is valid before sending a response.
    vm.getSecretMessage = function() {
      $http.get('http://localhost:3001/api/private', {headers : {
        Authorization: 'Bearer ' + localStorage.getItem('access_token')
      }}).then(function(response) {
        vm.message = response.data.message;
      }).catch(function(error){
        vm.message = "You must be logged in to access this resource."
      });
    }

  }

})();

From our home controller we will make calls to our API service. We will have two API calls, one for a public API route that anyone can access, and one for a protected route that can only be accessed successfuly by a logged in user. It’s ok if some of this code doesn’t make sense yet. We’ll dive deeper in the next section when we create our authentication service.

Creating the Authentication Service

We’ve referenced the authentication service multiple times so far but haven’t actually built it. Let’s take care of that next. The authentication service will be responsible for logging in users, managing the authentication state, and so on. Create a new directory called auth and in there a file auth.service.js. Our authentication service will look like this:

// auth.service.js
(function () {

  'use strict';

  angular
    .module('app')
    .service('authService', authService);

  authService.$inject = ['$state', 'angularAuth0', 'authManager'];

  function authService($state, angularAuth0, authManager) {

    // When a user calls the login function they will be redirected
    // to Auth0's hosted Lock and will provide their authentication
    // details.
    function login() {
      angularAuth0.authorize();
    }

    // Once a user is successfuly authenticated and redirected back
    // to the AngularJS application we will parse the hash to extract
    // the idToken and accessToken for the user.
    function handleParseHash() {
      angularAuth0.parseHash(
        { _idTokenVerification: false },
        function(err, authResult) {
        if (err) {
          console.log(err);
        }
        if (authResult && authResult.idToken) {
          setUser(authResult);
        }
      });
    }

    // This function will destroy the access_token and id_token
    // thus logging the user out.
    function logout() {
      localStorage.removeItem('access_token');
      localStorage.removeItem('id_token');
    }

    // If we can successfuly parse the id_token and access_token
    // we wil store them in localStorage thus logging the user in
    function setUser(authResult) {
      localStorage.setItem('access_token', authResult.accessToken);
      localStorage.setItem('id_token', authResult.idToken);
    }

    // This method will check to see if the user is logged in by
    // checking to see whether they have an id_token stored in localStorage
    function isAuthenticated() {
      return authManager.isAuthenticated();
    }

    return {
      login: login,
      handleParseHash: handleParseHash,
      logout: logout,
      isAuthenticated: isAuthenticated
    }
  }
})();

The authentication service is pretty straightforward. We have functions to handle the logging in and out of the application, as well as checking to see if the user is logged in. Our application should now work. Let’s go ahead and visit localhost:8080 to see our application in action.

If all went well you should see the Angular application load up and you will be in the logged out state.

angular authentication auth0

The two links at the bottom will not work at the moment since we haven’t deployed a server yet. We’ll do that shortly, but to make sure our app works, let’s try to login. Click either the login link in the nabar or in the main content of the page and you will be redirected to a login page on the Auth0 domain.

Auth0 login

Here you can login with any connection you have setup or even sign up for a new account. Login however you want and you will be redirected back to your AngularJS application at localhost:8080 but this time you will be in a logged in state.

Auth0 logged in

Excellent. For our last part of the demo, let’s write a simple Node server to handle our API calls.

Create the NodeJS Server

Let’s now quickly set up a NodeJS server so that we can make requests! Create a new directory called server and then install some dependencies.

mkdir server && cd server
npm init
npm install express express-jwt cors jkws-rsa

After installation, create an express app that uses the express-jwt middleware. You’ll need your Auth0 API information. Since we already created the API earlier, go into your dashboard, find the API, and copy it’s audience value. Take a look at the implementation below:

// server/server.js

var express = require('express');
var app = express();
var jwt = require('express-jwt');
var jwks = require('jwks-rsa');
var cors = require('cors');

app.use(cors());

var authCheck = jwt({
    secret: jwks.expressJwtSecret({
      cache: true,
      rateLimit: true,
      jwksRequestsPerMinute: 5,
      jwksUri: "https://{YOUR-AUTH0-DOMAIN}.auth0.com/.well-known/jwks.json"
    }),
    audience: '{YOUR-AUTH0-API-AUDIENCE}', // Paste your API audience here.
    issuer: "https://{YOUR-AUTH0-DOMAIN}.auth0.com/",
    algorithms: ['RS256']
});

app.get('/api/public', function(req, res) {
  res.json({ message: "Hello from a public endpoint! You don't need to be authenticated to see this." });
});

// For the private call we are using the authCheck middleware to validate the token
app.get('/api/private', authCheck, function(req, res) {
  res.json({ message: "Hello from a private endpoint! You DO need to be authenticated to see this." });
});

app.listen(3001);
console.log('Listening on http://localhost:3001');

The express-jwt middleware is used to protect endpoints from being accessed unless a valid JWT is sent. We then just need to apply the middleware to whichever routes we want to protect by passing it in as the second argument, just like we’ve done for the private route here.

Making API Requests

Start the server in a new console window/tab with the command node server.js

Now if we got to our AngularJS application now, and click the Get Message button, we will see a message displayed saying “Hello from the public endpoint…”. Click the Get Secret Message button next, and you should see the “Hello from private endpoint…” message displayed. This is because we logged in earlier and you are still in the logged in state.

access public endpoint

Let’s see what happens when you are not logged in and try to access the secret message. Click the Logout button from either the navbar or main content. Once you are logged out, click the Get Secret Message button and this time you will be presented with a different message saying that you must be authenticated before you can access the endpoint.

Access private endpoint

More on Auth0

Auth0 also makes it easy for us to add other modern authentication features to our apps, including single sign-on, passwordless login, and multifactor authentication.

We also aren’t limited to using NodeJS as our backend. There are SDKs available for many others, including:

There are also SDKs available for mobile development to make authentication easy:

Wrapping Up

Adding authentication to an API and sending authenticated requests to it from an AngularJS app is relatively easy, but there are quite a few steps involved in making sure the user experience is right. Auth0 does the heavy lifting for us when it comes to authentication, because we don’t need to worry about keeping our own database of users, nor do we need to put in our own login box.

From my experience, it’s much easier to implement authentication in an Angular 2 app because there are fewer things we need to worry about. If you are interested in Angular 2, you can check out this sample on how to build a working app with authentication, and also take a look at the Auth0 documentation.

  • Guilherme

    I’ve never dealt with secure data like passwords going to another endpoint not being on my server. How secure is the login transaction? It seems hat the login information does not go through the server, but does it require any SSL connection?

    • ryanchenkie

      Good question! Calls from the Lock widget to Auth0’s API happen over SSL. In terms of calling another API with passwords, it’s a bit like using a social auth provider like Google or Facebook–you need to send passwords to a third party.

      It’s recommended to use SSL for single page apps that use JWT authentication in all cases. There are options for redirecting the user to Auth0 to render the Lock widget, but this isn’t really used that often. In any case, the flow is secure :)

  • Henry L. Daehnke III

    I think your connection to Auto0 my be secure (it’s going over SSL), but the connection to your backend NodeJS server is not. At least I didn’t see it going over HTTPS. If that’s true then someone else (not authorized) can capture your JWT and use it against your RESTful web service. Seems like you need SSL all the time on each call for the “bearer” JWT token to be secure.

    • ryanchenkie

      Definitely, you’ll want to protect your app with SSL in all cases when using JWT :)

  • sangbum.woo

    The same circular dependency error too.

    • ryanchenkie

      Something must have changed in one of the supporting libraries. Try adding this to your app.js file in the config block.

      function redirect($q, $injector, $timeout, store, $location) {

      var auth;
      $timeout(function() {
      auth = $injector.get('auth');
      });

  • ryanchenkie

    Just posted a link to Github that’s awaiting moderation. Check out my reply to the comment above :)

  • ckapilla

    Nice article, thanks. I’ve been reviewing at Auth0 and it all looks very good, but one thing I can’t grok is how I can connect the Auth0 login to an existing database of pre-existing users. Can you provide any insight on that?

  • Francisco Vieira

    Very nice article, but I have a question, would you know why this Lock options are not working for my case?

    It just doesn’t work, nothing changes with the options I pass:

    var lock = new Auth0Lock(AUTH0_CLIENT_ID, AUTH0_DOMAIN, {
    initialScreen: ‘login’,
    allowLogin: true,
    allowedConnections: [“auth0”, “github”, “google-oauth2”, “twitter”, “facebook”],
    closable: false,
    avatar: true,
    theme: {
    logo: “img/car-icon.png”,
    primaryColor: ‘#E28706’
    },
    language: ‘pt-BR’
    });
    lock.show();

    It only shows the default login screen from Auth0

    • ryanchenkie

      it looks like your setup is correct. Do you have a sample that reproduces the issue?

  • Casey Burnett

    @ryanchenkie:disqus , maybe my thinking is off. Here’s what I’d like to do but can’t figure out how. I’ve successfully login and set up this Angular / Auth0 login. ps the video series was fantastic.

    Yet, I want to grab the user object from the login and pass it to my server. However as angular is handling the routing the ‘profile’ is not pinged on server.

    thoughts on how to get user data to node server for session? do I scrap the jwt from header? do I scrap angular login and follow auth0 node.js login. Is there part of ‘express-jwt’ that has access to user that is returned?

    • ryanchenkie

      What is it that you’re wanting to do with the user data on the server exactly? What do you mean by session? If you’re doing JWT authentication then you typically don’t want a traditional session on the server to handle authentication, but rather just a middleware that checks the JWT.

      • Casey Burnett

        @ryanchenkie:disqus thanks for replying.

        I have a lot of business logic to run and need to grab the user info as the profile view is loaded in the SPA. Make sense? event: user login -> auth0 authenticates -> grab user info -> ping DB, run calculations on server and push to view.

        I haven’t been able to use Express routes start the operations b/c, as I understand, SPA doesn’t ping the server when the profile view is loaded./

        any and all input is much appreciated. (especially if I’m barking up the wrong tree)

        • ryanchenkie

          One way that might work is to make a call to your server immediately after the user authenticates. You can send the user’s info to your backend and ping your DB, all while displaying a loading spinner. Once you have that response back, you can redirect to the view you want.

          Another approach that could work is to use the rules pipeline to augment the user’s JWT and/or profile. You can use rules to make a request as part of the authentication process. This will execute before the JWT and profile are returned to your app from Auth0. More info here: https://auth0.com/docs/rules

          • Casey Burnett

            Thanks. Yes, as I’ve been sitting with it a bit more, I’m now leaning towards setting up a server as a ‘watcher’ of the state that will ping DB and get the ball rolling. Your input has been most helpful.

          • ryanchenkie

            Awesome, let me know how it goes :)

  • ryanchenkie

    Looks like that would work. What security concerns do you have?

    • Casey Burnett

      more just ignorant fear. I wondered if $http would reveal the user info to any snoopers.

      • ryanchenkie

        You’ll definitely want to serve your app and your API over HTTPS to prevent any man in the middle attacks.

  • shahroze nawaz

    Indeed Auth0 is great authentication library I have tried it with one of my Symfony project and it worked like a charm :)

Recommended
Sponsors
Get the latest in JavaScript, once a week, for free.