cf-auth-middleware
Advanced tools
Comparing version 1.0.1 to 1.1.0
52
auth.js
module.exports = createMiddleware | ||
module.exports.validSignature = validSignature | ||
var getCredentials = require('./get-credentials') | ||
@@ -88,3 +90,4 @@ , createSignature = require('cf-signature') | ||
var valid = validSignature(req, authPacket, key, creds.signature) | ||
var valid = validSignature(req, authPacket, key, creds.signature | ||
, { logger: logger, ignoreQueryKeys: options.ignoreQueryKeys }) | ||
@@ -103,29 +106,36 @@ if (!valid) { | ||
/* | ||
* Sign the request and see if it matches the signature | ||
* the client sent. Returns true if it matches, false if not. | ||
*/ | ||
function validSignature(req, authPacket, key, theirSig) { | ||
} | ||
if (!key) return false | ||
/* | ||
* Sign the request and see if it matches the signature | ||
* the client sent. Returns true if it matches, false if not. | ||
*/ | ||
function validSignature(req, authPacket, key, theirSig, options) { | ||
options = options || {} | ||
// We use a URL without the auth querystring info for the signature | ||
var urlParts = url.parse(req.url, true) | ||
; delete urlParts.search | ||
; delete urlParts.query.authorization | ||
; delete urlParts.query['x-cf-date'] | ||
options.ignoreQueryKeys = options.ignoreQueryKeys || [] | ||
var contentType = req.headers['content-type'] ? req.headers['content-type'].split(';')[0] : '' | ||
, ourSig = createSignature(key, req.method, contentType, authPacket.date, url.format(urlParts)) | ||
, requestDate = (new Date(authPacket.date)).getTime() | ||
, currentDate = Date.now() | ||
, difference = Math.abs(currentDate - requestDate) | ||
if (!key) return false | ||
logger.debug('Comparing:', ourSig, theirSig) | ||
logger.debug('Request Time: ' + requestDate + ' Current Time: ' + currentDate + ' Difference: ' + difference) | ||
// We use a URL without the auth querystring info for the signature | ||
var urlParts = url.parse(req.url, true) | ||
; delete urlParts.search | ||
; delete urlParts.query.authorization | ||
; delete urlParts.query['x-cf-date'] | ||
return (theirSig === ourSig) && difference < 60000 | ||
options.ignoreQueryKeys.forEach(function (key) { | ||
delete urlParts.query[key] | ||
}) | ||
} | ||
var contentType = req.headers['content-type'] ? req.headers['content-type'].split(';')[0] : '' | ||
, ourSig = createSignature(key, req.method, contentType, authPacket.date, url.format(urlParts)) | ||
, requestDate = (new Date(authPacket.date)).getTime() | ||
, currentDate = Date.now() | ||
, difference = Math.abs(currentDate - requestDate) | ||
options.logger.debug('Comparing:', ourSig, theirSig) | ||
options.logger.debug('Request Time: ' + requestDate + ' Current Time: ' + currentDate + ' Difference: ' + difference) | ||
return (theirSig === ourSig) && difference < 60000 | ||
} |
@@ -5,3 +5,3 @@ { | ||
"description": "Authentication middleware for APIs using cf-auth-provider", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"tags": [], | ||
@@ -8,0 +8,0 @@ "repository": { |
@@ -25,3 +25,3 @@ # cf-auth-middleware | ||
An authenticated request contains the following headers: | ||
An authenticated request must contain either the following headers: | ||
@@ -34,14 +34,12 @@ ``` | ||
The client must sign the request with the following algorithm: | ||
***OR*** | ||
```js | ||
var crypto = require('crypto') | ||
It must contain the following query string keys: | ||
function createSignature(key, method, contentType, date, path) { | ||
var hmac = crypto.createHmac('sha1', key) | ||
, packet = method + '\n\n' + (contentType || '') + '\n' + date + '\n\n' + path | ||
return hmac.update(packet).digest('base64') | ||
} | ||
``` | ||
?authorization={authorizing entity id}:{signed request}&x-cf-date=1423481045233 | ||
``` | ||
The client must sign requests with the [cf-signature](https://github.com/clocklimited/cf-signature) module. | ||
## API | ||
@@ -59,2 +57,4 @@ | ||
- `options.reqProperty`: the authed client's id is stored on the request object: `req[options.reqProperty]`. Defaults to `authedClient`. | ||
- `options.ignoreQueryKeys`: an array of keys to ignore when comparing the request to the signature. | ||
This is useful when requests get augmented by unknown cache-busting values. Defaults to `[]`. | ||
@@ -61,0 +61,0 @@ ## Credits |
@@ -9,2 +9,3 @@ var request = require('supertest') | ||
, assert = require('assert') | ||
, createSignature = require('cf-signature') | ||
@@ -177,4 +178,4 @@ function noop() { | ||
var app2 = express() | ||
app.use(createMiddleware(authProvider, { logger: noopLogger })) | ||
app.use(function (req, res, next) { | ||
app2.use(createMiddleware(authProvider, { logger: noopLogger })) | ||
app2.use(function (req, res, next) { | ||
assert.equal(req.authedClient, authedAdministrator._id) | ||
@@ -199,4 +200,4 @@ next() | ||
var app2 = express() | ||
app.use(createMiddleware(authProvider, { logger: noopLogger, reqProperty: 'ohla' })) | ||
app.use(function (req, res, next) { | ||
app2.use(createMiddleware(authProvider, { logger: noopLogger, reqProperty: 'ohla' })) | ||
app2.use(function (req, res, next) { | ||
assert.equal(req.ohla, authedAdministrator._id) | ||
@@ -235,2 +236,22 @@ next() | ||
it('should allow you to ignore certain querystring keys', function (done) { | ||
var app2 = express() | ||
app2.use(createMiddleware(authProvider, { logger: noopLogger, ignoreQueryKeys: [ 'ignored' ] })) | ||
createRoutes(app2) | ||
var date = (new Date()).toUTCString() | ||
, hash = createSignature(authedAdministrator.key, 'GET', '', date, '/') | ||
request(app2) | ||
.get('/?ignored=1') | ||
.set('x-cf-date', date) | ||
.set('Authorization', 'Catfish ' + authedAdministrator._id + ':' + hash) | ||
.end(function (error, res) { | ||
assert.equal(res.statusCode, 200) | ||
done() | ||
}) | ||
}) | ||
describe('querystring base authentication', function() { | ||
@@ -256,1 +277,30 @@ | ||
}) | ||
describe('#validSignature()', function () { | ||
var validSignature = createMiddleware.validSignature | ||
it('should be exported as a separate function for use in non-express auth', function () { | ||
assert.equal(typeof validSignature, 'function') | ||
}) | ||
it('should allow ignoring of other querystring keys', function () { | ||
var method = 'GET' | ||
, date = new Date().getTime() | ||
, path = '/a/b/c' | ||
, request = { url: path + '?d=1&e=2', method: method, headers: {} } | ||
, authPacket = { date: date } | ||
, key = '123' | ||
, signature = createSignature(key, method, '', date, path) | ||
, invalid = validSignature(request, authPacket, key, signature, { logger: noopLogger }) | ||
, valid = validSignature(request, authPacket, key, signature | ||
, { logger: noopLogger, ignoreQueryKeys: [ 'd', 'e' ] }) | ||
// Request is invalid when query string is taken into account | ||
assert.equal(invalid, false) | ||
// But valid when query string keys are ignored | ||
assert.equal(valid, true) | ||
}) | ||
}) |
20335
13
439