Managing basic authentication

In any real-world Ember application, at some point, you'll need to deal with authentication. For example, users might need to send their credentials to identify themselves to the server, or authenticated users may need access to protected parts of the application.

An important aspect of authentication is protecting information based on the logged in user. This can be done by creating sessions with the use of tokens. In this recipe, we'll create a simple token-based authentication with an Express server. This will help us understand the basics. In the next section, we'll go over using OAuth2 with Ember Simple Auth.

How to do it...

  1. In a new application, generate these files:
    $ ember g service session
    $ ember g adapter application
    $ ember g controller login
    $ ember g model student name:string age:number
    $ ember g route login
    $ ember g route students
    $ ember g template index
    $ ember g server index
    $ npm install body-parser –save-dev
    

    This will generate all the scaffolding that we need for our application. The students route will be protected. It will only populate information from the server if the user is authenticated.

    The ember g server index command will generate a node Express mock server for us. In the previous chapters, we used Ember CLI Mirage to do all our mock tests. The Express server generated by Ember is not as powerful. However, it will be easier to set up our fake server and authentication example. Be aware that when deploying an Ember application to production, the Ember server will not be included. It will, however, automatically start when running the ember serve command.

  2. Create a new service called session that will handle our authentication and keep track of authenticated users:
    // app/services/session.js
    import Ember from 'ember';
    
    export default Ember.Service.extend({
        token: null,
        authenticate(log, pass) {
          return Ember.$.ajax({
            method: 'POST',
            url: '/token',
            data: { username: log, password: pass}
          }).then((info)=>{
            this.set('token',info.access_token);
          });
        }
    
    });

    This service will be injected into our login controller. This will keep track of authenticated users and send Ajax requests to the server. The authenticate method accepts the login name and password. These values are sent to the server using an HTTP POST method to /token. If the login information is correct, a token is returned and saved. If not, an error will be returned. We'll deal with the error in the login controller later.

    The token property will be used to keep track whether the user is authenticated or not.

  3. Update the Express server index.js file. Add a route for token:
    // server/index.js
    /*jshint node:true*/
    
    const bodyParser = require('body-parser');
    
    
    module.exports = function(app) {
        app.post('/token', function(req, res) {
    
          if (req.body.username === 'erik' && req.body.password === 'password') {
            res.send({ access_token: "secretcode" });
          } else {
            res.status(400).send({ error: "invalid_grant" });
          }
    
        });
    };

    This is our node Express server. It will run when we start the Ember server. When the HTTP POST /token request is sent to the server, it will check the body username and password. For this example, we'll just hardcode them as 'erik' and 'secretcode'. If these match, it returns access_token. If not, it returns an invalid message.

    The access token will be saved in the session service. We can use this token to authenticate future requests to the server.

  4. Update the application.js file. Add a new authorization header:
    // app/adapters/application.js
    import DS from 'ember-data';
    import Ember from 'ember';
    
    export default DS.RESTAdapter.extend({
        namespace: 'api',
        session: Ember.inject.service(),
        headers: Ember.computed('session.token',function(){
          return {
            'Authorization': `Bearer ${this.get('session.token')}`
          };
        })
    });

    In our application, accessing the students route will trigger a request to the server. The server will respond to authenticated users only. The server expects an authorization bearer header with every Ember Data request. We can do this using the headers computed property and returning the Authorization: Bearer header with the secret token from the service. Every request to the server, using Ember Data, will send this header.

  5. Update the Express server index information in order to return information to the application if the token matches:
    // app/server/index.js
    …
        app.use(bodyParser.urlencoded({ extended: true }));
    
        app.get('/api/students', function (req, res) {
    
          if (req.headers.authorization !== "Bearer secretcode") {
            return res.status(401).send('Unauthorized');
          }
    
          return res.status(200).send({
            students: [
              { id: 1, name: 'Erik', age: 24 },
              { id: 2, name: 'Suze', age: 32 },
              { id: 3, name: 'Jill', age: 18 }
            ]
    
          });
        });
    …

    Above app.post, you can add app.get for the students route. Ember will trigger this HTTP GET request whenever it enters the /students route. The server will check whether the request header has the secret code. If it matches, it returns the proper JSON data for the students route. If not, it returns a 401 error.

  6. Verify the route and model information for students:
    // app/models/student.js
    import DS from 'ember-data';
    
    export default DS.Model.extend({
        name: DS.attr('string'),
        age: DS.attr('number')
    });
  7. Earlier, we generated this file with two properties, name and age:
    // app/routes/students.js
    import Ember from 'ember';
    
    export default Ember.Route.extend({
        model() {
          return this.store.findAll('student');
        }
    });
  8. The students route will send an HTTP GET request to /students to retrieve the students model. As we defined the application to use the REST adapter, Ember will expect the data in the REST format:
    // app/templates/students.hbs
    Secret Student Information
    {{#each model as |student|}}
        {{student.name}}<br>
        {{student.age}}<br>
    {{/each}}

    The students template uses the each helper to iterate through the model returned from the server. It displays each name and age.

  9. Add the login username and property information to the login template:
    // app/templates/login.hbs
    <h3>Login</h3>
    
    {{input value=loginName placeholder='Login'}}<br>
    {{input value=password placeholder='Password' type='password'}}<br>
    <button {{action 'authenticate'}}>Login</button>

    This template uses the input helper for the loginName and password properties. The button triggers the authenticate action.

  10. Update the login controller to handle the authenticate action:
    // app/controllers/login.js
    import Ember from 'ember';
    
    export default Ember.Controller.extend({
        loginName: null,
        password: null,
        session: Ember.inject.service(),
        actions: {
          authenticate(){
            this.get('session').authenticate(this.get('loginName'), this.get('password')).then( ()=>{
              alert('Great you are logged in!');
              this.transitionToRoute('students');
            },(err)=>{
              alert('Error! Problem with token! '+ err.responseText);
            });
          }
        }
    });

    The controller has several properties. It retrieves the session information service and uses the authenticate method to send the login information to the server. The authenticate method returns a promise. If it's successful, the application transitions to the students route. If it's not successful, an error is displayed in an alert box. In this example, we are using the ES6 arrow function. The ()=> arrow function is a little shorter than using a function expression and it also lexically binds this variable.

  11. Update the index with a link to log in:
    // app/templates/index.hbs
    Welcome to my app! Login {{#link-to 'login'}}here{{/link-to}}

    This is just a simple link to the login route.

  12. Start the server and navigate to the login route. You'll see the following image:
    How to do it...
  13. To log in, enter the username 'erik' and password 'password'. After clicking Login, an HTTP POST request will be sent to the server with the name and password information. The Express server that we had set up earlier will respond with the token, and the session service will save it. The application will then transition to the students route. The following screen will be displayed:
    How to do it...
  14. When this route loads, an HTTP GET request will be sent to /students. The Express server will check to make sure that the authorization bearer header has the correct secret code. It then will respond with the JSON data that Ember will display.
  15. If the username or password does not match, the following alert box will be displayed:
    How to do it...

How it works...

Token-based authentication requires the client to send over credentials to the server. If authorized, the server then sends back a token that the client saves and uses on subsequent requests to the server. If the token is not present, the server may not return data to the client.

This recipe is a simple example of using authentication. It lacks proper error handling and session persistence. Nevertheless, it gives you an idea of how authentication works.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset