Monday 10 April 2017

Implementing proper authentication with passport-http

I'm relatively new to using passport and am currently trying to implement passport-http for user authentication on my node.js api. (Previously used jwt tokens with no problems).

It's not clear to me what passport-http should be doing. Right now I have a route for registration that looks like this:

routes/user.js

router.post('/', sanitize, validate, function(req, res, next) {
    const user = new User({
        username: req.body.username,
        email: req.body.email,
        password: bcrypt.hashSync(req.body.password, config.secret_num)
    });

    //Check if e-mail or username already exists
    const userExistsPromise = User.findOne({username: user.username});
    const emailExistsPromise = User.findOne({email: user.email});

    Promise.all([userExistsPromise, emailExistsPromise]).then(function(docs) {
        if(docs[0]) { //If we find a doc where username matches
            return sendJsonResponse(res, false, "This username already exists.", 409);
        }
        if(docs[1]) { //If we find a doc where email matches
            return sendJsonResponse(res, false, "This email address already exists.", 409);
        }
        user.save(function(err) {
            if(err) {
                console.log(err);
                return sendJsonResponse(res, false, "There was an error processing your request.", 400);
            }
            return sendJsonResponse(res, true, "Successfully registered user.");
        });
    }, function(err) {
        console.log(err);
        return sendJsonResponse(res, false, "There was an error processing your request.", 400);
    });
});

This simply adds a username, e-mail and password to the db. My first question is do I need to do this via passport somehow?

My current passport middleware is defined as follows:

middleware/auth.js

// Load required packages
const passport = require('passport');
const BasicStrategy = require('passport-http').BasicStrategy;
const User = require('../models/user');

passport.use(new BasicStrategy(
    function(username, password, callback) {
        User.findOne({ username: username }, function (err, user) {
            if (err) { return callback(err); }

            // No user found with that username
            if (!user) { return callback(null, false); }

            // Make sure the password is correct
            user.verifyPassword(password, function(err, isMatch) {
                if (err) { return callback(err); }

                // Password did not match
                if (!isMatch) { return callback(null, false); }

                // Success
                return callback(null, user);
            });
        });
    }
));

exports.isAuthenticated = passport.authenticate('basic', { session : false });

And I have a test route that looks like this:

const auth = require('../middleware/auth');
router.get('/test', auth.isAuthenticated, function(req, res, next) {
    return sendJsonResponse(res, true, "This is a test get only for authenticated users");
});

This works fine when I pass a valid/invalid username and password as a Basic Auth using Postman. So I correctly get "invalid credentials" when credentials are invalid, and I get "This is a test get only for authenticated users" when I have valid credentials.

Great, except how would I have the login credentials in my Basic Auth header in the first place?

I have a login route like this:

router.get('/', auth.isAuthenticated, function(req, res, next) {
    return res.json(req.user);
});

But this doesn't look right because I'm sending back a username, email, and hashed password... so 1) This is probably not a good idea. 2) This won't even work because I can't just put the username/pass in the header for the next request.

Shouldn't I send some kind of header or cookie or session that'll tell me on the next request that the user has valid credentials or invalid credentials? I don't understand how to fill in this gap.

Edit: So as I wrote this question, and thought about it, I think I might have been over-complicating it in my head. Seems like the intuitive way is to store the credentials in a cookie or local storage on the client side and use basic auth header every time I make a request that requires credentials... does that sound about right?

Still unsure if passing credentials via basic auth like this is secure though.



via snanda

No comments:

Post a Comment