Question

I've got a route in my Express 3.0 app defined like this:

app.post('/:app/find', auth, function(req, res){

I'd like it restricted using HTTP authorization. My auth function just looks like this:

var auth = express.basicAuth(function(user, pass, callback){
    callback(null, user === 'user' && pass === 'pass');
});

I'd like the auth function to do a database lookup based on req.params.app from the URL, and authenticate users accordingly. The problem is that I'm not sure how to access that yet, since the function defined in app.post hasn't been called yet when auth is run. Where can I get it? Is there a different implementation of HTTP auth I should be using?

Was it helpful?

Solution

There isn't a proper way of doing that with express.basicAuth (I also looked at some other modules that provide similar functionality, but they never seem to pass req to the authentication function either), but you could create a middleware which instantiates it for every request that you want to authenticate:

var express = require('express');
var app     = express();

var basicAuth = express.basicAuth;
var auth      = function(req, res, next) {
  ...here you can access 'req'...
  basicAuth(function(user, pass, callback) {
    ...here you can use variables set in the block above...
    callback(null, user === 'user' && pass === 'pass');
  })(req, res, next);
};

app.get('/', auth, function(req, res) {
  res.send('authenticated!');
});

app.listen(3012);

OTHER TIPS

I found a similar question here on StackOverflow, but had some trouble getting the answer provided to work. I converted it to middleware to make it fit in more nicely:

var includeAuth = function(req, res, next){
    var header = req.header('authorization', false);
    if(header){
        var token = header.split(/\s+/).pop() || '';
        if(token.length > 0){
            var auth = new Buffer(token, 'base64').toString(),
                parts = auth.split(/:/);
            req.auth = {user: parts[0], pass: parts[1]};
        }else
            req.auth = false;
    }else
        req.auth = false;
    next();
}

app.configure(function(){
    app.use(includeAuth);
});

...but req.header.authorization was never set. I was stumped, until I came across this gist, which hinted at the fact that even if the client was sending authorization headers, I still had to request them. So I added this:

if(req.auth){
        // Do things
}else{
    res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
    res.statusCode = 401;
    res.end("NO_CREDENTIALS");
}

I then started seeing data in req.auth, but the object had the properties "username" and "password" rather than "user" and "pass" as I had specified. After a little more poking around, I discovered that... those are put there by Express automatically! Or so it seems, anyway. I made a new application without any middleware or other frills, and got the same results, so long as I sent that WWW-Authenticate header. This rendered my includeAuth middleware function useless. Armed with my new-found knowledge, I can now use the following to provide specific error messages to the client when they try to authenticate:

function verifyAuth(req, res, appName, callback){
    if(req.auth){
        db.apps.findOne({id: appName}, function(err, app){ // Call to MongoDB
            if(!err && app){
                if(app.user === req.auth.username && app.pass === req.auth.password)
                    callback(null, true);
                else
                    callback("INVALID_CREDENTIALS", false);
            }else
                callback("NO_SUCH_APP", false);
        });
    }else{
        res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
        res.statusCode = 401;
        res.end(JSON.stringify({success: false, error: "NO_CREDENTIALS"}));
        callback(null, false);
    }
}

app.post('/:app/find', function(req, res){
    verifyAuth(req, res, req.params.app, function(err, allowed){
        if(!err && allowed){
            // Do some things
        }else if(err)
            res.send({success: false, error: err});
    });
});

However, I'm going to give Robert the correct answer, since his solution not only works perfectly, but also uses less (and simpler) code to get there, and will lend itself better to someone else's project, should they stumble across this question.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top