cf-auth-middleware
Advanced tools
Comparing version 0.0.1 to 0.1.0
38
auth.js
@@ -12,17 +12,27 @@ module.exports = createMiddleware | ||
if (typeof authProvider.authenticate !== 'function') { | ||
throw new Error('Expecting an authenticate function') | ||
} | ||
return function auth(req, res, next) { | ||
if (typeof authProvider.lookupKey !== 'function') { | ||
throw new Error('Expecting a lookupKey function') | ||
} | ||
return middleware | ||
/* | ||
* The auth checking middleware. Verifies all request methods | ||
* except for OPTIONS. If verifcation passes, next() is called | ||
* and the rest of the route handling stack will be executed. | ||
* If verification fails, the middleware ends the response with | ||
* a 401 and a header www-authenticate=Catfish. | ||
*/ | ||
function middleware(req, res, next) { | ||
// Don't auth options, used in CORS preflight | ||
if (req.method === 'OPTIONS') return next() | ||
if (typeof authProvider.authenticate !== 'function') { | ||
throw new Error('Expecting a authenticate function') | ||
} | ||
if (typeof authProvider.lookupKey !== 'function') { | ||
throw new Error('Expecting a lookupKey function') | ||
} | ||
verify(req, function (err, success) { | ||
// Don't worry about err, only allow request to complete if success=true | ||
// otherwise respond with a 401 and authenticate header | ||
if (success) return next() | ||
@@ -35,2 +45,5 @@ res.header('www-authenticate', 'Catfish') | ||
/* | ||
* Verify that the request was signed by an authenticated user. | ||
*/ | ||
function verify(req, cb) { | ||
@@ -62,2 +75,6 @@ | ||
/* | ||
* Sign the request and see if it matches the signature | ||
* the client sent. Returns true if it matches, false if not. | ||
*/ | ||
function validSignature(req, key, sig) { | ||
@@ -67,7 +84,6 @@ | ||
// Added replace charset to fix content-type being different in Firefox for POST requests causing hash !== sig | ||
var urlParts = url.parse(req.url) | ||
, hmac = crypto.createHmac('sha1', key) | ||
, packet = req.method | ||
+ '\n\n' + (req.headers['content-type'] ? req.headers['content-type'].replace('; charset=UTF-8', '') : '') | ||
+ '\n\n' + (req.headers['content-type'] ? req.headers['content-type'].split(';')[0] : '') | ||
+ '\n' + req.headers['x-cf-date'] + '\n\n' + urlParts.pathname | ||
@@ -74,0 +90,0 @@ , hash = hmac.update(packet).digest('base64') |
@@ -5,3 +5,3 @@ { | ||
"description": "Authentication middleware for APIs using cf-auth-provider", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"tags": [], | ||
@@ -8,0 +8,0 @@ "repository": { |
@@ -29,8 +29,23 @@ var request = require('supertest') | ||
it('should respond with 401 if bad credentials are supplied (POST)', function (done) { | ||
it('should error if the provided authProvider lacks an authenticate() function', function () { | ||
(function () { | ||
createMiddleware({}) | ||
}).should.throw('Expecting an authenticate function') | ||
}) | ||
it('should error if the provided authProvider lacks a lookupKey() function', function () { | ||
(function () { | ||
createMiddleware({ authenticate: function () {} }) | ||
}).should.throw('Expecting a lookupKey function') | ||
}) | ||
it('should respond with 401 if no credentials are supplied (POST)', function (done) { | ||
var r = request(app) | ||
.post('/') | ||
.set('Accept', 'application/json') | ||
.send({ identity: 'paul.serby@clock.co.uk', password: 'password' }) | ||
.end(function (error, res) { | ||
@@ -44,3 +59,3 @@ res.statusCode.should.equal(401) | ||
it('should respond with 401 if bad credentials are supplied (GET)', function (done) { | ||
it('should respond with 401 if no credentials are supplied (GET)', function (done) { | ||
var r = request(app) | ||
@@ -57,7 +72,7 @@ .get('/') | ||
it('should respond with 401 if not authenticated and bad credentials are supplied', function (done) { | ||
it('should respond with 401 if bad credentials are supplied', function (done) { | ||
var r = request(app) | ||
.get('/') | ||
.set('Accept', 'application/json') | ||
.set('authorization', 'BAD') | ||
.set('authorization', 'Catfish x:y') | ||
.expect(401) | ||
@@ -106,4 +121,47 @@ .end(function (error, res) { | ||
}) | ||
}) | ||
// This is skipped until https://github.com/visionmedia/superagent/pull/284 is merged and supertest is updated | ||
// because .set('Content-Type', x) can't hande the charset part. | ||
it.skip('should work when the charset parameter of the Content-Type header is set', function (done) { | ||
var date = (new Date()).toUTCString() | ||
, hash = createSignature(authedAdministrator.key, 'POST', 'application/json', date, '/') | ||
var r = request(app) | ||
.post('/') | ||
.send({ some: 'Date', onThe: 'POST request' }) | ||
.set('Accept', 'application/json') | ||
.set('x-cf-date', date) | ||
.set('Authorization', 'Catfish ' + authedAdministrator._id + ':' + hash) | ||
.set('Content-Type', 'application/json; charset=utf-8') | ||
.end(function (error, res) { | ||
res.statusCode.should.equal(200) | ||
r.app.close() | ||
done() | ||
}) | ||
}) | ||
it('should not try to authenticate OPTIONS requests', function (done) { | ||
var r = request(app) | ||
.options('/') | ||
.end(function (error, res) { | ||
res.statusCode.should.equal(200) | ||
r.app.close() | ||
done() | ||
}) | ||
}) | ||
it('should fail to authenticate if the authProvider errors while looking up the key', function (done) { | ||
var r = request(app) | ||
.get('/') | ||
.set('Authorization', 'Catfish fail:xyz') | ||
.end(function (error, res) { | ||
res.statusCode.should.equal(401) | ||
r.app.close() | ||
done() | ||
}) | ||
}) | ||
}) |
@@ -15,2 +15,3 @@ module.exports = createAuthProvider | ||
function lookupKey(id, cb) { | ||
if (id === 'fail') return cb(new Error('uh oh')) | ||
cb(null, sessions[id]) | ||
@@ -17,0 +18,0 @@ } |
12481
267