Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

EmberJS Tutorial, Part 2: Authentication

DZone's Guide to

EmberJS Tutorial, Part 2: Authentication

We finish up developing our EmberJS application by using a free resource, Auth0, to create authentication forms for our application.

· Web Dev Zone
Free Resource

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Welcome back! If you missed Part 1, check it out here

Adding Authentication to Your EmberJS 2 App

The majority of the apps we use on a daily basis have a means of authenticating users. I'll show you how to easily add authentication to our EmberJS 2 application. We'll use Auth0 as our authentication service.

Auth0 allows us to issue JSON Web Tokens (JWTs). If you don't already have an Auth0 account, sign up for a free one now.

Login to your Auth0 management dashboard and let's create a new API client. If you don't already have the APIs menu item, you can enable it by going to your Account Settings and in the Advanced tab, scroll down until you see the Enable APIs Section and flip the switch.

From here, click on the APIs menu item and then the Create API button. You will need to give your API a name and an identifier. The name can be anything you choose, so make it as descriptive as you want. The identifier will be used to identify your API, this field cannot be changed once set. For our example, I'll name the API Startup Battle API and for the identifier, I'll set it as http://whistleblower.com. We'll leave the signing algorithm as RS256 and click on the Create API button.

Creating the Whistle Blower APICreating the Whistle Blower API

Next, let's define some scopes for our API. Scopes allow us to manage access to our API. We can define as few or as many scopes as we want. For our simple example, we'll just create a single scope that will grant users full access to the API.

Locate scopes barLocate Scopes bar

Adding Scope to APIAdding scope

Secure The Node API

We need to secure the API so that the meetups and whistle blowers endpoints will only be accessible to authenticated users. We can secure it easily with Auth0.

Open up your server.js file and add the authCheck middleware to the private battles endpoint like so:

app.get('/api/meetups', authCheck, (req,res) => {
  let meetups = [
    // Array of meetups
  ];

  res.json(meetups);
})

app.get('/api/whistleblowers', authCheck, (req,res) => {
  let whistleBlowers = [
    // Array of whistle blowers
  ];

  res.json(whistleBlowers);
})

app.listen(3333);
console.log('Listening on localhost:3333');

Try accessing the http://localhost:3333/api/meetups endpoint again from Postman. You should be denied access like so:

Unauthorized AccessUnauthorized Access

Next, let's add authentication to our front-end.

Adding Authentication to Our EmberJS 2 Front-End

We created an auth service earlier. Open up app/services/auth.js again.

In the code present in this file, we are using a hosted version of Auth0 Lock in the login method. We also passed in our credentials.

The Auth0 node module called Auth0's authorize endpoint. With all the details we passed to the method, our client app will be validated and authorized to perform authentication. You can learn more about the specific values that can be passed to the authorize method here.

The parameters that you do not have yet are the {AUTH0-CLIENT-ID} and the {CALLBACK-URL}. When you created your API, Auth0 also created a test client which you can use. Additionally, you can use any existing Auth0 client found in Clients section of your management dashboard.

Check the Test panel of your API from the dashboard. You'll see the test client like so:

Startup ClientWhistleBlower API Client

Now, go to the client's area and check for the test client. Open the client and change the Client Type from Non Interactive Client to Single Page Application.

Non-interactive clients are meant to be used in machine to machine interactions. We are using an SPA to interact with the API so the client should be an SPA client. Check out Implicit Grant and client credentials exchange for more information.

Copy the CLIENT ID from the dashboard and replace it with the value of the CLIENT-ID constant variable in your code. Replace your callback URL with http://localhost:4200/callback. Replace your client domain, scope, and audience values with the domain from your Auth0 dashboard, and the scope from your API and API identifier respectively.

We checked whether the token has expired via the getTokenExpirationDateand isTokenExpired methods. The isLoggedIn method returns true or false based on the presence and validity of a user id_token.

Let's go update the app-nav component to hide/show the login and logoutbuttons based on the user's authentication status.

Now, your app-nav component template file should look like this:

<nav class="navbar navbar-default">
    <div class="navbar-header">
      {{#link-to 'index' class="navbar-brand"}} Whistle Blower {{/link-to}}
    </div>

    <ul class="nav navbar-nav">
        <li>
            {{#if loggedIn }}
                {{#link-to 'meetups' class="navbar-brand"}} Meetups {{/link-to}}
                {{#link-to 'whistle-blowers' class="navbar-brand"}} Whistle Blowers {{/link-to}}
            {{/if}}
        </li>
    </ul>

    <ul class="nav navbar-nav navbar-right">
      <li>
        {{#if loggedIn }}
            <button {{ action "logout" }} class="btn btn-danger log">Log out</button>
        {{else}}
            <button {{ action "login" }} class="btn btn-info log">Log In</button>
        {{/if}}
      </li>
    </ul>
</nav>

app/templates/components/app-nav.hbs

Now, we used some handlebars helpers in the code above such as #if for conditional operations, #link-to for linking to a route.

We have a loggedIn variable that is set as a flag for alternating the display of the login and logout buttons. Now, where did that variable come from?

Open the component file responsible for the app-nav template and add this:

import Ember from 'ember';

export default Ember.Component.extend({
    auth: Ember.inject.service('auth'),

    init() {
        this._super(...arguments);
        this.set('loggedIn', this.get('auth').isLoggedIn());
    },

    actions: {
        login() {
            this.get('auth').login();
        },
        logout() {
            this.get('auth').logout();
        }
    }
});

app/components/app-nav.js

We injected the auth service. Then we called the init method which is a component hook that is called whenever a component has been initialized. In the init method, we set a variable loggedIn with the value of the user's authentication status taken from the auth service.

this._super(..arguments) has to be called to override the init method that ships with Ember components.

Next, we defined the actions. In Ember, components use actions to communicate events and changes. The methods defined in the actions object get called when the user needs to trigger an action on the UI either via buttons or any form field elements.

In our app-nav template, we have the logout and login buttons that trigger some actions:

{{#if loggedIn }}
  <button {{ action "logout" }} class="btn btn-danger log">Log out</button>
{{else}}
  <button {{ action "login" }} class="btn btn-info log">Log In</button>
{{/if}}

Activate the Callback Route

We created a callback route earlier. This route will be called when a redirect is made from Auth0 after a successful authentication. The route will process the redirect and store the access_token and id_token coming from Auth0. Open app/routes/callback.js and add this:

import Ember from 'ember';

export default Ember.Route.extend({

    auth: Ember.inject.service('auth'),

    beforeModel() {
        this.get('auth').setAccessToken();
        this.get('auth').setIdToken();
        this.transitionTo('/');
    },
});

app/routes/callback.js

Once a user is authenticated, Auth0 will redirect back to our application and call the /callback route. Auth0 will also append the id_token as well as the access_token to this request, and our callback route will make sure to properly process and store those tokens in localStorage. If all is well, meaning we received an id_token, called access_token, we will be redirected back to the / page and will be in a logged in state.

Ember Routes provide a beforeModel hook that is called before the model is initialized. It can be used to conditionally prevent access to a route. It can also be used to perfom some actions like a middleware.

Add Some Values to Auth0 Dashboard

Just before you try to log in or sign up, head over to your Auth0 dashboard and add http://localhost:4200/callback to the Allowed Callback URLs and http://localhost:4200 to Allowed Origins (CORS) of your client.

Secure Meetups and Whistleblowers Routes

We need to ensure that no one can go to the browser and just type /meetupsand whistle-blowers to access the meetups and whistleblowers routes respectively.

Open up app/routes/meetups.js. Modify it to have the beforeModel hook and inject the auth service:

import Ember from 'ember';

export default Ember.Route.extend({

    auth: Ember.inject.service('auth'),

    api: Ember.inject.service('whistleblowerapi'),

    beforeModel() {
        if(!this.get('auth').isLoggedIn()) {
            this.transitionTo('/');
        }
    },

    model() {
        return this.get('api').getMeetups();
    }
});

app/routes/meetups.js

Open up app/routes/whistle-blowers.js. Modify it to also have the beforeModel hook and inject the auth service:

import Ember from 'ember';

export default Ember.Route.extend({

    auth: Ember.inject.service('auth'),

    api: Ember.inject.service('whistleblowerapi'),

    beforeModel() {
        if(!this.get('auth').isLoggedIn()) {
            this.transitionTo('/');
        }
    },

    model() {
        return this.get('api').getWhistleBlowers();
    }
});

app/routes/whistle-blowers.js

In both routes, just before the model initializes, we check if the user is logged in. If the user has not been authenticated, we redirect the user back to the landing page.

Now, try to log in.

Lock Login WidgetLock Login Widget

For the first time, the user will be shown a user consent dialog that will show the scopes available. Once a user authorizes, it goes ahead to log in the user and give them access based on the scopes.

User consent dialogUser presented with an option to authorize

Note: Since we are using localhost for our domain, once a user logs in the first time, subsequent logins will not need a user consent authorization dialog. This consent dialog will not be displayed if you are using a non-localhost domain, and the client is a first-party client.

Logged In and Athorized to see the private meetups pageLogged In and authorized see the private meetups page

Logged In and Athorized to see the private whistleblowers pageLogged In and authorized to see the private whistleblowers page

We have successfully logged in and can access the content of both private routes. We passed an option to send an Authorization header with a Bearer access_token along with the GET request in our API service. The request sends the JWT to the secured backend. The backend verifies it. If it is valid, the user is granted access to the resources.

...
  getMeetups() {
        const url = `${BASE_URL}/api/meetups`;
        return axios.get(url, { headers: { Authorization: `Bearer ${this.getAccessToken()}` }}).then(response => response.data);
    },

    getWhistleBlowers() {
        const url = `${BASE_URL}/api/whistleblowers`;
        return axios.get(url, { headers: { Authorization: `Bearer ${this.getAccessToken()}` }}).then(response => response.data);
    },
...

What happens if we don't send an access_token? Go ahead and remove the Authorization header. Make a plain get request that doesn't send any JWT to the backend.

Aha, we get a blank page. The Chrome Dev tools screaming at us like:

Unauthorized401 Unauthorized

Important API Security Note: Anytime you intend to use Auth0 authentication to authorize API requests, note that you'll need to use a different flow depending on your use case. Auth0 id_token should only be used on the client-side. Access tokens should be used to authorize APIs. You can read more about making API calls with Auth0 here.

Conclusion

You have just successfully built an EmberJS 2 app and added authentication to it. To be honest, Ember has a steep learning curve. It takes a while to navigate where to find different files and put certain logic but once you get a hang of it, then it becomes a Saber for architecting and building ambitious web applications.

In addition, Auth0 can help secure your EmberJS apps with more than just username-password authentication. It provides features like multifactor authanomaly detectionenterprise federationsingle sign on (SSO), and more.

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
web dev ,authentication ,web application development ,emberjs

Published at DZone with permission of Prosper Otemuyiwa, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}