Skip to content
This repository has been archived by the owner on Oct 30, 2018. It is now read-only.

Commit

Permalink
Switch from incremental nonce to unique nonce cache
Browse files Browse the repository at this point in the history
  • Loading branch information
niahmiah committed May 22, 2016
1 parent db6f30c commit d4b5a37
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 18 deletions.
5 changes: 2 additions & 3 deletions doc/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ of the request. For GET, DELETE, and OPTIONS requests, you must sign the raw
query string parameters.

In addition to the parameters required for each individual request, you must
also include a `__nonce` parameter. This value should be an integer and must be
incremented with every request. A common practice is to simply use the current
UNIX timestamp.
also include a `__nonce` parameter. This value should be a string and must be
unique with every request. A common practice is to simply use UUID v4.

In addition to the request parameters and nonce, you will also sign the HTTP
method and request path. Ultimately the string you will sign will be:
Expand Down
24 changes: 15 additions & 9 deletions lib/server/middleware/authenticate.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const fromQuery = ['GET', 'DELETE', 'OPTIONS'];
function AuthenticateMiddlewareFactory(storage) {
const PublicKey = storage.models.PublicKey;
const User = storage.models.User;
const UserNonce = storage.models.UserNonce;

function authenticate(req, res, next) {
let strategy = AuthenticateMiddlewareFactory._detectStrategy(req);
Expand Down Expand Up @@ -74,18 +75,23 @@ function AuthenticateMiddlewareFactory(storage) {
));
}

if (!params.__nonce || params.__nonce < user.__nonce) {
return next(new errors.NotAuthorizedError(
'Invalid nonce supplied'
));
}
var userNonce = new UserNonce({
user: user.id,
nonce: params.__nonce
});

user.__nonce = params.__nonce;
userNonce.save(function(err) {
if (err && err.code === '11000') {
return next(new errors.NotAuthorizedError(
'Invalid nonce supplied'
));
}

req.user = user;
req.pubkey = pubkey;
req.user = user;
req.pubkey = pubkey;

user.save(next);
return next(err);
});
});
});
break;
Expand Down
1 change: 1 addition & 0 deletions lib/storage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Storage.prototype._createBoundModels = function() {
Bucket: require('./models/bucket'),
PublicKey: require('./models/pubkey'),
User: require('./models/user'),
UserNonce: require('./models/usernonce'),
Token: require('./models/token'),
Contact: require('./models/contact'),
Shard: require('./models/shard'),
Expand Down
6 changes: 0 additions & 6 deletions lib/storage/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ var User = new mongoose.Schema({
activated: {
type: Boolean,
default: false
},
// incremented on API request
__nonce: {
type: Number,
default: 0
}
});

Expand All @@ -44,7 +39,6 @@ User.set('toObject', {
transform: function(doc, ret) {
delete ret.__v;
delete ret._id;
delete ret.__nonce;
delete ret.hashpass;
delete ret.activator;
}
Expand Down
29 changes: 29 additions & 0 deletions lib/storage/models/usernonce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

const mongoose = require('mongoose');
const SchemaOptions = require('../options');

/**
* Represents a unique user & nonce
* @constructor
*/
var UserNonce = new mongoose.Schema({
user: {
type: mongoose.SchemaTypes.Email,
ref: 'User',
required: true
},
nonce: {
type: String,
required: true,
expires: '5m'
},
});

UserNonce.plugin(SchemaOptions);

UserNonce.index({ user: 1, nonce: 1 }, {unique: true});

module.exports = function(connection) {
return connection.model('UserNonce', UserNonce);
};

0 comments on commit d4b5a37

Please sign in to comment.