Timi Ajiboye

Building API Authentication from Scratch - Part 2

Today, we're gonna continue building simple authentication for our Sails API from scratch.

You can check out all the source code for this tutorial on github here.

In the first post in this series we created our User model and our registration endpoint.

In this post we're going to do three things:

  1. Create an endpoint /login to log in.
  2. Create a policy that would check request headers to see if they have a correct Authorization token before letting the request through.
  3. Apply the policy above.

Login

We need to create an AuthController.js file in the controllers folder.
In it we're going to have a login action that signs a User in and returns it's token.

module.exports = {

  login: function (req, res) {
    var email = req.param('email');
    var password = req.param('password');

    verifyParams(res, email, password)

    User.findOne({email: email}).then(function (user) {
      if (!user) {
        return invalidEmailOrPassword(res);
      }
      signInUser(req, res, password, user)
    }).catch(function (err) {
      return invalidEmailOrPassword(res);
    })
  }

};


function signInUser(req, res, password, user) {  
  User.comparePassword(password, user).then(
    function (valid) {
      if (!valid) {
        return this.invalidEmailOrPassword();
      } else {
        var responseData = {
          user: user,
          token: generateToken(user.id)
        }
        return ResponseService.json(200, res, "Successfully signed in", responseData)
      }
    }
  ).catch(function (err) {
    return ResponseService.json(403, res, "Forbidden")
  })
};


function invalidEmailOrPassword(res){  
  return ResponseService.json(401, res, "Invalid email or password")
};

function verifyParams(res, email, password){  
  if (!email || !password) {
    return ResponseService.json(401, res, "Email and password required")
  }
};


function generateToken(user_id) {  
  return JwtService.issue({id: user_id})
};

This action makes use of the comparePassword function we created in the first post, in the User.js model file.

In our routes.js file, we add.

'post /login': 'AuthController.login',  

Our endpoint is now done.

isAuthorized Policy

Now we create a file api/policies/isAuthorized.js.

module.exports = function (req, res, next) {  
  let token;

  if (req.headers && req.headers.authorization) {
    var parts = req.headers.authorization.split(' ');
    if (parts.length == 2) {
      var scheme = parts[0],
        credentials = parts[1];

      if (/^Bearer$/i.test(scheme)) {
        token = credentials;
      }
    } else {
      return ResponseService.json(401, res, "Format is Authorization: Bearer [token]");
    }
  } else if (req.param('token')) {
    token = req.param('token');

    delete req.query.token;
  } else {
    return ResponseService.json(401, res, "No authorization header was found");
  }

  JwtService.verify(token, function(err, decoded){
    if (err) return ResponseService.json(401, res, "Invalid Token!");
    req.token = token;
    User.findOne({id: decoded.id}).then(function(user){
      req.current_user = user;
      next();
    })
  });

}

The above code does the following:

  • Checks if the incoming request has Authorization included in it's headers.
  • Checks if the format of the header is correct as in Authorization: Bearer [token].
  • Next it verifies the token using our JwtService.
  • If the token is verified and decoded, it then finds the user that corresponds to that token.
  • It adds the user as a current_user parameter to the request so that current_user shall be made available to controllers.

Now we can apply the policy to controllers in /config/policies.js.

module.exports.policies = {

  '*': ['isAuthorized'],

  'UserController': {
    'create': true
  },

  'AuthController': {
    '*': true
  }
};

This blocks all requests except:

  • It's either to create a user or to login.
  • The request isAuthorized

You can read more about policies in the hellosails post about them.

That's it.
Remember, you can check out all the source code for this tutorial on github here.

O dabọ.