Sunday, October 4, 2015

Passport-http Digest Authentication and Express 4+ bug fix

Passport is quite popular environment for implementing authentication under Node.JS and Express Framework.

Passport-http is a plug-in that provides Digest Authentication interfaces and is very popular for Passport.

However, since Express 4, the default approach to the Express routes is them to be relative.

Example - 
Express <=3 default application assumed that every file who extend Express with routes should specify the full path to the route in a similar to this approach:

app.js:
app.get('{fullpath}',function)...

Or:
require('routes/users')(app)

Where users.js do as well:

app.get('{fullpath',function)...

The default app of Express 4+ uses relative routes, which is quite better as it allows full isolation between the modules of the application.

Example:

app.js:
app.use('/rest',require('routes/rest'));

Where rest.js has:

router = require('express').Router();
router.get('/data', function)... // where data is relative url and the actual will be /rest/data

This simplifies the readability. Also it allows you to separate the authentication. You could have different authentication approach or configuration to each different module. And that works well with Passport.

For example:

app.js:
app.use(passport.initialize());
app.use(passport.session());

rest.js:
var DigestStrategy = require('passport-http').DigestStrategy;
... here there should be a code for authentication function using Digest ...

and then:
router.get('/data',authentication,function) ....

This simplifies, makes it more readable and isolates very much the code necessary for authentication.

Personally, I write my own authentication functions in a separate module, then I include them in the express route module where I want to use them and it became even more simpler:

rest.js:
var auth = require('../libs/auth.js');
Router.get('/data', auth('admins'), function) ...

I even could apply different permissions, roles like - if you have pre authenticated session, then the interface will not ask you for authentication (saved one RTT) but if you don't it will ask you for digest authentication. Quite simple and quite readable.

However, all this does not work with Passport-http, because of a very small bug within.

The bug:

For security reasons, passport-http module verifies that the authentication URI from the customer request is the same as the URL requested authentication. However, the authentication URI (creds.uri) is always full path, but it is compared to req.url which is always relative path. The comparision has to be between creds.uri and req.baseUrl+req.url.

And this is my fix proposed to the author of passport-http, which I hope will be merged with the code.