Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .jscsrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

{
"preset": "node-style-guide",
"requireCamelCaseOrUpperCaseIdentifiers": false,
"disallowMultipleVarDecl": { allExcept: ['require'] }
}
8 changes: 5 additions & 3 deletions examples/simple_express4/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@ myOAP.on('lookup_grant', function(client_id, client_secret, code, next) {

// embed an opaque value in the generated access token
myOAP.on('create_access_token', function(user_id, client_id, next) {
var extra_data = 'blah'; // can be any data type or null
//var oauth_params = {token_type: 'bearer'};
var extra_data = {id: 1}; // can be any data type or null
var oauth_params = {
client_secret: 'abc'
};

next(extra_data/*, oauth_params*/);
next(extra_data, oauth_params);
});

// (optional) do something with the generated access token
Expand Down
204 changes: 121 additions & 83 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,30 @@
*/

var EventEmitter = require('events').EventEmitter,
querystring = require('querystring'),
serializer = require('serializer');
jwtDecode = require('jwt-decode'),
jwt = require('jwt-simple'),
querystring = require('querystring'),
serializer = require('serializer'),
_ = require('underscore'),
moment = require('moment');

_extend = function(dst,src) {

var srcs = [];
if ( typeof(src) == 'object' ) {
srcs.push(src);
} else if ( typeof(src) == 'array' ) {
for (var i = src.length - 1; i >= 0; i--) {
srcs.push(this._extend({},src[i]))
};
} else {
throw new Error("Invalid argument")
}

for (var i = srcs.length - 1; i >= 0; i--) {
for (var key in srcs[i]) {
dst[key] = srcs[i][key];
}
};

return dst;
}
function parse_authorization(authorization) {
if(!authorization)
if (!authorization) {
return null;
}

var parts = authorization.split(' ');

if(parts.length != 2 || parts[0] != 'Basic')
if (parts.length !== 2 || parts[0] !== 'Basic') {
return null;
}

var creds = new Buffer(parts[1], 'base64').toString(),
i = creds.indexOf(':');

if(i == -1)
if (i === -1) {
return null;
}

var username = creds.slice(0, i);
password = creds.slice(i + 1);
Expand All @@ -52,12 +38,12 @@ function parse_authorization(authorization) {
}

function OAuth2Provider(options) {
if(arguments.length != 1) {
if (arguments.length !== 1) {
console.warn('OAuth2Provider(crypt_key, sign_key) constructor has been deprecated, yo.');

options = {
crypt_key: arguments[0],
sign_key: arguments[1],
sign_key: arguments[1]
};
}

Expand All @@ -71,45 +57,49 @@ function OAuth2Provider(options) {
OAuth2Provider.prototype = new EventEmitter();

OAuth2Provider.prototype.generateAccessToken = function(user_id, client_id, extra_data, token_options) {
token_options = token_options || {}
var out = _extend(token_options, {
access_token: this.serializer.stringify([user_id, client_id, +new Date, extra_data]),
refresh_token: null,
});
return out;
token_options = token_options || {};

var access_token, refresh_token;
var client_secret = token_options.client_secret || this.options.crypt_key;
if (token_options.client_secret) {
// Unset client_secret as it's redundant in payload.
delete token_options.client_secret;
}

// JWT access_token
access_token = jwt.encode(_.extend(extra_data, token_options), client_secret);
refresh_token = this.serializer.stringify([user_id, client_id, parseInt(moment().unix(), 10)]);

// Ensure payload conforms with RFC 6749 spec.
var payload = {
access_token: access_token,
token_type: 'bearer',
expires_in: token_options.exp - parseInt(moment().unix(), 10),
refresh_token: refresh_token
};

return payload;
};

OAuth2Provider.prototype.login = function() {
var self = this;

return function(req, res, next) {
var data, atok, user_id, client_id, grant_date, extra_data;
var atok, tokenBody;
atok = self._getTokenFromReq(req);

if(req.query['access_token']) {
atok = req.query['access_token'];
} else if((req.headers['authorization'] || '').indexOf('Bearer ') == 0) {
atok = req.headers['authorization'].replace('Bearer', '').trim();
} else {
if (!atok) {
return next();
}

try {
data = self.serializer.parse(atok);
user_id = data[0];
client_id = data[1];
grant_date = new Date(data[2]);
extra_data = data[3];
} catch(e) {
res.writeHead(400);
return res.end(e.message);
tokenBody = self._getTokenBody(atok);

// Check whether token has expired.
if (tokenBody.exp && tokenBody.exp < parseInt(moment().unix(), 10)) {
return res.status(400).send('Access token invalid or expired');
}

self.emit('access_token', req, {
user_id: user_id,
client_id: client_id,
extra_data: extra_data,
grant_date: grant_date
}, next);
self.emit('access_token', req, res, tokenBody, atok, next);
};
};

Expand All @@ -119,11 +109,11 @@ OAuth2Provider.prototype.oauth = function() {
return function(req, res, next) {
var uri = ~req.url.indexOf('?') ? req.url.substr(0, req.url.indexOf('?')) : req.url;

if(req.method == 'GET' && self.options.authorize_uri == uri) {
var client_id = req.query.client_id,
if (req.method === 'GET' && self.options.authorize_uri === uri) {
var client_id = req.query.client_id,
redirect_uri = req.query.redirect_uri;

if(!client_id || !redirect_uri) {
if (!client_id || !redirect_uri) {
res.writeHead(400);
return res.end('client_id and redirect_uri required');
}
Expand All @@ -139,12 +129,13 @@ OAuth2Provider.prototype.oauth = function() {
self.emit('authorize_form', req, res, client_id, authorize_url);
});

} else if(req.method == 'POST' && self.options.authorize_uri == uri) {
var client_id = (req.query.client_id || req.body.client_id),
redirect_uri = (req.query.redirect_uri || req.body.redirect_uri),
response_type = (req.query.response_type || req.body.response_type) || 'code',
state = (req.query.state || req.body.state),
x_user_id = (req.query.x_user_id || req.body.x_user_id);
} else if (req.method === 'POST' && self.options.authorize_uri === uri) {
var response_type = (req.query.response_type || req.body.response_type) || 'code',
state = (req.query.state || req.body.state),
x_user_id = (req.query.x_user_id || req.body.x_user_id);

redirect_uri = (req.query.redirect_uri || req.body.redirect_uri);
client_id = (req.query.client_id || req.body.client_id);

var url = redirect_uri;

Expand All @@ -156,8 +147,8 @@ OAuth2Provider.prototype.oauth = function() {
return res.end('invalid response_type requested');
}

if('allow' in req.body) {
if('token' == response_type) {
if ('allow' in req.body) {
if ('token' === response_type) {
var user_id;

try {
Expand All @@ -169,10 +160,10 @@ OAuth2Provider.prototype.oauth = function() {
return res.end(e.message);
}

self.emit('create_access_token', user_id, client_id, function(extra_data,token_options) {
self.emit('create_access_token', user_id, client_id, function(extra_data, token_options) {
var atok = self.generateAccessToken(user_id, client_id, extra_data, token_options);

if(self.listeners('save_access_token').length > 0)
if (self.listeners('save_access_token').length > 0)
self.emit('save_access_token', user_id, client_id, atok);

url += querystring.stringify(atok);
Expand All @@ -185,12 +176,13 @@ OAuth2Provider.prototype.oauth = function() {

self.emit('save_grant', req, client_id, code, function() {
var extras = {
code: code,
code: code
};

// pass back anti-CSRF opaque value
if(state)
if (state) {
extras['state'] = state;
}

url += querystring.stringify(extras);

Expand All @@ -204,17 +196,17 @@ OAuth2Provider.prototype.oauth = function() {
res.writeHead(303, {Location: url});
res.end();
}
} else if (req.method === 'POST' && self.options.access_token_uri === uri) {
var client_secret = req.body.client_secret,
code = req.body.code;

} else if(req.method == 'POST' && self.options.access_token_uri == uri) {
var client_id = req.body.client_id,
client_secret = req.body.client_secret,
redirect_uri = req.body.redirect_uri,
code = req.body.code;
redirect_uri = req.body.redirect_uri;
client_id = req.body.client_id;

if(!client_id || !client_secret) {
if (!client_id || !client_secret) {
var authorization = parse_authorization(req.headers.authorization);

if(!authorization) {
if (!authorization) {
res.writeHead(400);
return res.end('client_id and client_secret required');
}
Expand All @@ -223,14 +215,14 @@ OAuth2Provider.prototype.oauth = function() {
client_secret = authorization[1];
}

if('password' == req.body.grant_type) {
if(self.listeners('client_auth').length == 0) {
if ('password' === req.body.grant_type) {
if (self.listeners('client_auth').length === 0) {
res.writeHead(401);
return res.end('client authentication not supported');
}

self.emit('client_auth', client_id, client_secret, req.body.username, req.body.password, function(err, user_id) {
if(err) {
if (err) {
res.writeHead(401);
return res.end(err.message);
}
Expand All @@ -243,7 +235,7 @@ OAuth2Provider.prototype.oauth = function() {
});
} else {
self.emit('lookup_grant', client_id, client_secret, code, function(err, user_id) {
if(err) {
if (err) {
res.writeHead(400);
return res.end(err.message);
}
Expand All @@ -252,7 +244,6 @@ OAuth2Provider.prototype.oauth = function() {

self._createAccessToken(user_id, client_id, function(atok) {
self.emit('remove_grant', user_id, client_id, code);

res.end(JSON.stringify(atok));
});
});
Expand All @@ -270,11 +261,58 @@ OAuth2Provider.prototype._createAccessToken = function(user_id, client_id, cb) {
this.emit('create_access_token', user_id, client_id, function(extra_data, token_options) {
var atok = self.generateAccessToken(user_id, client_id, extra_data, token_options);

if(self.listeners('save_access_token').length > 0)
if (self.listeners('save_access_token').length > 0) {
self.emit('save_access_token', user_id, client_id, atok);
}

return cb(atok);
});
};

// Prep Jwt body object.
OAuth2Provider.prototype.prepTokenBody = function(iss, sub, aud, exp) {
var body = {
iss: iss,
sub: sub,
aud: aud,
iat: parseInt(moment().unix(), 10),
};

// Set expiration
if (exp && exp.key && exp.value) {
body.exp = parseInt(moment().add(exp.value, exp.key).unix(), 10);
}

return body;
};

// Helper function to get the bearer token from the authorization header.
OAuth2Provider.prototype._getTokenFromReq = function(req) {
var pattern = /(Token|Bearer)\s/gi;

if (!req.headers.authorization && !pattern.test(req.headers.authorization)) {
return null;
} else {
return req.headers.authorization.replace(pattern, '');
}
};

// Helper function for decoding JWT Token.
OAuth2Provider.prototype.verifyToken = function(token, secret) {
try {
if (jwt.decode(token, secret)) {
return true;
}
return false;
} catch(e) {
console.log(e);
return false;
}
};

// Helper function for decoding JWT Token.
OAuth2Provider.prototype._getTokenBody = function(token) {
return jwtDecode(token);
};

exports.OAuth2Provider = OAuth2Provider;
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
},
"main": "index",
"dependencies": {
"serializer": ">=0.0.2 <0.1.0"
"jwt-simple": "^0.3.1",
"serializer": ">=0.0.2 <0.1.0",
"underscore": "^1.8.3",
"moment": "~2.11.1",
"jwt-decode": "~1.5.1"
},
"devDependencies": {
"mocha" : "1.0.3"
,"sinon" : "1.3.4"
,"chai" : "1.0.3"
"mocha": "1.0.3",
"sinon": "1.3.4",
"chai": "1.0.3"
},
"licenses": [
{
Expand Down