3

I am creating an production angularJs application.

Now ive created parts of my RESTFul API

this API generates a user object on login however i am not keen on sending the password unhashed / un incrypted over the HTTP header.

To give you some insight of my API:

on login:

    var jwt = require('jwt-simple');

// Route: /login
module.exports = function (express, sequelize, router) {

    var DataTypes = require("sequelize");
    var crypto = require('crypto');

    var User = sequelize.define('user', {
            id: DataTypes.INTEGER,
            username: DataTypes.STRING,
            name: DataTypes.STRING,
            organization_id: DataTypes.INTEGER,
            type_id: DataTypes.INTEGER,
            division_id: DataTypes.INTEGER
        }, {
            freezeTableName: true,
            instanceMethods: {
                retrieveByNamePassword: function (username, onSuccess, onError) {
                    User.find({where: {username: username}}, {raw: true})
                        .then(onSuccess).catch(onError);
                }
            }
        }
    );

    router.route('/login')

        .post(function (req, res) {
            var user = User.build();

            var username = req.body.username || '';
            var password = req.body.password || '';

            if (username == '' || password == '') {
                res.status(401);
                res.json({
                    "status": 401,
                    "message": "Invalid credentials"
                });
                return;
            }

            user.retrieveByNamePassword(username, function (users) {
                if (users) {
                    // If authentication is success, we will generate a token
                    // and dispatch it to the client
                    res.json(genToken(users));
                    return;
                } else {
                    // If authentication fails, we send a 401 back
                    res.status(401);
                    res.json({
                        "status": 401,
                        "message": "Invalid credentials"
                    });
                    return;
                }
            }, function (error) {
                res.status(401);
                res.json({
                    "status": 401,
                    "message": "Invalid credentials"
                });
                return;
            });
        });

    var genToken = function(user) {
        var expires = expiresIn(7); // 7 days
        var token = jwt.encode({
            exp: expires,
            user_id: user.id,
            organization_id: user.organization_id,
            type_id: user.type_id,
            division_id: user.division_id

        }, require('./config/secret')());

        return {
            token: token,
            expires: expires,
            user: user
        };
    };

    var expiresIn = function(numDays) {
        var dateObj = new Date();
        return dateObj.setDate(dateObj.getDate() + numDays);
    };

As you can see right now (Because i am not sure yet how to handle it) am only looking for the username and then checks that the username exists.

My question is fairly simple how do you go around encrypting the password and then validate it once it reaches the API?

Marc Rasmussen
  • 19,771
  • 79
  • 203
  • 364

2 Answers2

2

JWT can be used with both encrypted and non-encrypted data. However regardless of whether or not you're encrypting the information you're transferring, there is always a signing process involved which allows you to verify that the information you're receiving hasn't been tampered with before you read it.

For the use case of transferring login state via the URL between domains, I would say not including the password at all in your JWT would be the best option.

Take the following process for example:

  1. POST your login details over HTTP to domain1.
  2. Authenticate the user. If successful, create a JWT containing at least:

    2.1. exp: expiry time (let's say current time + 10 to 15 seconds at most)

    2.2. iat: token creation time

    2.3. email: user's email address

    2.4. do not include the users password (or any other sensative data).

  3. Redirect your user to your domain2 with your JWT token in the URL

  4. Your other server/domain can check the JWT signature, iat and exp times to verify that the JWT has not been tampered with and is valid.
  5. If it is all valid, log your user in to the other domain.

This works because as long as the JWT data validates, the second domain doesn't actually need to know your users password, because domain1 server has already validated the login and is simply telling domain2 to accept it. So long as your JWT secret key is kept completely secret, this should be fairly secure. I would also encourage having an automated method of randomly changing your secret key on both servers periodically if possible as well.

Note: with this method, anyone that gets hold of the JWT can view the data contained in it - so do not transfer any confidential or sensative information there.

Note 2: This method would be vulnerable to a replay attack for whatever time period you set in your expiry field of the JWT. If a third party can get hold of the token during that timeframe, they would be able to also use the token to log themselves in as that user. For this reason, you must keep the expiry field as low as possible to reduce the risk of this.

Joel Cox
  • 3,289
  • 1
  • 14
  • 31
  • So i cannot avoid posting username and password over HTTP? What if i use a socket connection and then push the elements to the socket? – Marc Rasmussen Feb 26 '15 at 11:45
  • @MarcRasmussen my answer mostly referred to using JWT and only signing the data to create the token. You can optionally encrypt the data you are sending prior to encoding it in the JWT (using public/private keys) and then decrypt it again when reading the token later. Complete information about this is available here: https://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-40 and whatever JWT library your using most likely has an easy method to allow you to do this. Note - I answered with the above because it's always better to not send sensitive data at all than it is to encrypt it. – Joel Cox Feb 26 '15 at 22:20
  • @Joel: Do you know what is the best practise to invalidate jwt while changing password/logout - http://stackoverflow.com/questions/28759590/best-practices-to-invalidate-jwt-while-changing-passwords-and-logout-in-node-js – Gopinath Shiva Feb 27 '15 at 07:38
1

This is super late, but could you not add a hook after the user model that uses crypt? (Like this: https://www.youtube.com/watch?v=pquxHIBx8ks)

// User model...
},   {
hooks: {
    beforeValidate: function() {

    },
    afterValidate: function(User) {
        User.hashword = bcrypt.hashSync(User.hashword, 10);



    },
    beforeCreate: function() {

    },
    afterCreate: function() {

    }
}
});
Natu Myers
  • 486
  • 2
  • 4
  • 21