From 77627b89a28924d757aae73bdabe6df5ba940697 Mon Sep 17 00:00:00 2001 From: Bradley Matusiak Date: Sun, 12 May 2013 21:59:30 -0400 Subject: [PATCH 1/6] oauth/access_token can use query --- index.js | 51 +++++++++++++++------------------------------------ 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/index.js b/index.js index 7fe7b5d..9c7bbde 100644 --- a/index.js +++ b/index.js @@ -9,27 +9,6 @@ var EventEmitter = require('events').EventEmitter, querystring = require('querystring'), serializer = require('serializer'); -_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) return null; @@ -70,17 +49,17 @@ 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, { +OAuth2Provider.prototype.generateAccessToken = function(user_id, client_id, extra_data) { + var out = { access_token: this.serializer.stringify([user_id, client_id, +new Date, extra_data]), refresh_token: null, - }); + }; + return out; }; OAuth2Provider.prototype.login = function() { - var self = this; + var __self = this; return function(req, res, next) { var data, atok, user_id, client_id, grant_date, extra_data; @@ -94,7 +73,7 @@ OAuth2Provider.prototype.login = function() { } try { - data = self.serializer.parse(atok); + data = __self.serializer.parse(atok); user_id = data[0]; client_id = data[1]; grant_date = new Date(data[2]); @@ -104,7 +83,7 @@ OAuth2Provider.prototype.login = function() { return res.end(e.message); } - self.emit('access_token', req, { + __self.emit('access_token', req, { user_id: user_id, client_id: client_id, extra_data: extra_data, @@ -169,8 +148,8 @@ OAuth2Provider.prototype.oauth = function() { return res.end(e.message); } - 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); + self.emit('create_access_token', user_id, client_id, function(extra_data) { + var atok = self.generateAccessToken(user_id, client_id, extra_data); if(self.listeners('save_access_token').length > 0) self.emit('save_access_token', user_id, client_id, atok); @@ -206,10 +185,10 @@ OAuth2Provider.prototype.oauth = function() { } } 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; + var client_id = req.body.client_id || req.query.client_id, + client_secret = req.body.client_secret || req.query.client_secret, + redirect_uri = req.body.redirect_uri || req.query.redirect_uri, + code = req.body.code || req.query.code; if(!client_id || !client_secret) { var authorization = parse_authorization(req.headers.authorization); @@ -267,8 +246,8 @@ OAuth2Provider.prototype.oauth = function() { OAuth2Provider.prototype._createAccessToken = function(user_id, client_id, cb) { var self = this; - 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); + this.emit('create_access_token', user_id, client_id, function(extra_data) { + var atok = self.generateAccessToken(user_id, client_id, extra_data); if(self.listeners('save_access_token').length > 0) self.emit('save_access_token', user_id, client_id, atok); From 469c089925abc0671edbc3ffeaf56d7221590e8b Mon Sep 17 00:00:00 2001 From: Bradley Matusiak Date: Sun, 12 May 2013 23:29:03 -0400 Subject: [PATCH 2/6] added another body + query for user token check --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 9c7bbde..d4aed3d 100644 --- a/index.js +++ b/index.js @@ -64,8 +64,8 @@ OAuth2Provider.prototype.login = function() { return function(req, res, next) { var data, atok, user_id, client_id, grant_date, extra_data; - if(req.query['access_token']) { - atok = req.query['access_token']; + if(req.query['access_token'] || req.body['access_token']) { + atok = req.query['access_token'] || req.body['access_token']; } else if((req.headers['authorization'] || '').indexOf('Bearer ') == 0) { atok = req.headers['authorization'].replace('Bearer', '').trim(); } else { From 18a9b58b38a762dc2f25287cf72dde56b4115c2f Mon Sep 17 00:00:00 2001 From: Bradley Matusiak Date: Wed, 15 May 2013 23:06:04 -0400 Subject: [PATCH 3/6] added emitters for authorize form --- index.js | 145 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 64 deletions(-) diff --git a/index.js b/index.js index d4aed3d..b74b6ef 100644 --- a/index.js +++ b/index.js @@ -114,76 +114,93 @@ OAuth2Provider.prototype.oauth = function() { // store user_id in an HMAC-protected encrypted query param authorize_url += '&' + querystring.stringify({x_user_id: self.serializer.stringify(user_id)}); - // user is logged in, render approval page - self.emit('authorize_form', req, res, client_id, authorize_url); + self.emit('before_authorize_form', req, res, function() { + self.emit('authorize_form', req, res, client_id, authorize_url); + },function(){ + var code = serializer.randomString(128); + self.emit('save_grant', req, client_id, code, function() { + var extras = { + code: code, + }; + var url = redirect_uri; + url += '?'; + // pass back anti-CSRF opaque value + if (state) extras['state'] = state; + url += querystring.stringify(extras); + res.writeHead(303, { + Location: url + }); + res.end(); + }); + }); }); } 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); - - var url = redirect_uri; - - switch(response_type) { - case 'code': url += '?'; break; - case 'token': url += '#'; break; + 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); + var url = redirect_uri; + switch (response_type) { + case 'code': + url += '?'; + break; + case 'token': + url += '#'; + break; default: - res.writeHead(400); - return res.end('invalid response_type requested'); - } - - if('allow' in req.body) { - if('token' == response_type) { - var user_id; - - try { - user_id = self.serializer.parse(x_user_id); - } catch(e) { - console.error('allow/token error', e.stack); - - res.writeHead(500); - return res.end(e.message); - } - - self.emit('create_access_token', user_id, client_id, function(extra_data) { - var atok = self.generateAccessToken(user_id, client_id, extra_data); - - if(self.listeners('save_access_token').length > 0) - self.emit('save_access_token', user_id, client_id, atok); - - url += querystring.stringify(atok); - - res.writeHead(303, {Location: url}); - res.end(); - }); - } else { - var code = serializer.randomString(128); - - self.emit('save_grant', req, client_id, code, function() { - var extras = { - code: code, - }; - - // pass back anti-CSRF opaque value - if(state) - extras['state'] = state; - - url += querystring.stringify(extras); - - res.writeHead(303, {Location: url}); + res.writeHead(400); + return res.end('invalid response_type requested'); + } + if ('allow' in req.body) { + self.emit('after_authorize_form', req, res, client_id,function(){ + if ('token' == response_type) { + var user_id; + try { + user_id = self.serializer.parse(x_user_id); + } + catch (e) { + console.error('allow/token error', e.stack); + res.writeHead(500); + return res.end(e.message); + } + self.emit('create_access_token', user_id, client_id, function(extra_data) { + var atok = self.generateAccessToken(user_id, client_id, extra_data); + if (self.listeners('save_access_token').length > 0) self.emit('save_access_token', user_id, client_id, atok); + url += querystring.stringify(atok); + res.writeHead(303, { + Location: url + }); + res.end(); + }); + } + else { + var code = serializer.randomString(128); + self.emit('save_grant', req, client_id, code, function() { + var extras = { + code: code, + }; + // pass back anti-CSRF opaque value + if (state) extras['state'] = state; + url += querystring.stringify(extras); + res.writeHead(303, { + Location: url + }); + res.end(); + }); + } + }); + } + else { + url += querystring.stringify({ + error: 'access_denied' + }); + res.writeHead(303, { + Location: url + }); res.end(); - }); } - } else { - url += querystring.stringify({error: 'access_denied'}); - - res.writeHead(303, {Location: url}); - res.end(); - } - } else if(req.method == 'POST' && self.options.access_token_uri == uri) { var client_id = req.body.client_id || req.query.client_id, client_secret = req.body.client_secret || req.query.client_secret, From d4e2669c36f749b15e41d253d5d41483eff00437 Mon Sep 17 00:00:00 2001 From: Bradley Matusiak Date: Thu, 16 May 2013 01:17:31 -0400 Subject: [PATCH 4/6] fix for event --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index b74b6ef..5491ea9 100644 --- a/index.js +++ b/index.js @@ -114,7 +114,7 @@ OAuth2Provider.prototype.oauth = function() { // store user_id in an HMAC-protected encrypted query param authorize_url += '&' + querystring.stringify({x_user_id: self.serializer.stringify(user_id)}); - self.emit('before_authorize_form', req, res, function() { + self.emit('before_authorize_form', req, res,client_id, function() { self.emit('authorize_form', req, res, client_id, authorize_url); },function(){ var code = serializer.randomString(128); From 12d31ceb2561c627ca0e2ebfe85aae29ca4a395e Mon Sep 17 00:00:00 2001 From: Bradley Matusiak Date: Sun, 25 Aug 2013 16:41:56 -0400 Subject: [PATCH 5/6] changed extra data to scope and allow passing of req to createAccessToken --- index.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index 5491ea9..5d16875 100644 --- a/index.js +++ b/index.js @@ -49,9 +49,9 @@ function OAuth2Provider(options) { OAuth2Provider.prototype = new EventEmitter(); -OAuth2Provider.prototype.generateAccessToken = function(user_id, client_id, extra_data) { +OAuth2Provider.prototype.generateAccessToken = function(user_id, client_id, scope) { var out = { - access_token: this.serializer.stringify([user_id, client_id, +new Date, extra_data]), + access_token: this.serializer.stringify([user_id, client_id, +new Date, scope]), refresh_token: null, }; @@ -62,7 +62,7 @@ OAuth2Provider.prototype.login = function() { var __self = this; return function(req, res, next) { - var data, atok, user_id, client_id, grant_date, extra_data; + var data, atok, user_id, client_id, grant_date, scope; if(req.query['access_token'] || req.body['access_token']) { atok = req.query['access_token'] || req.body['access_token']; @@ -77,7 +77,7 @@ OAuth2Provider.prototype.login = function() { user_id = data[0]; client_id = data[1]; grant_date = new Date(data[2]); - extra_data = data[3]; + scope = data[3]; } catch(e) { res.writeHead(400); return res.end(e.message); @@ -86,7 +86,7 @@ OAuth2Provider.prototype.login = function() { __self.emit('access_token', req, { user_id: user_id, client_id: client_id, - extra_data: extra_data, + scope: scope, grant_date: grant_date }, next); }; @@ -165,8 +165,8 @@ OAuth2Provider.prototype.oauth = function() { res.writeHead(500); return res.end(e.message); } - self.emit('create_access_token', user_id, client_id, function(extra_data) { - var atok = self.generateAccessToken(user_id, client_id, extra_data); + self.emit('create_access_token', req, user_id, client_id, function(scope) { + var atok = self.generateAccessToken(user_id, client_id, scope); if (self.listeners('save_access_token').length > 0) self.emit('save_access_token', user_id, client_id, atok); url += querystring.stringify(atok); res.writeHead(303, { @@ -233,7 +233,7 @@ OAuth2Provider.prototype.oauth = function() { res.writeHead(200, {'Content-type': 'application/json'}); - self._createAccessToken(user_id, client_id, function(atok) { + self._createAccessToken(req, user_id, client_id, function(atok) { res.end(JSON.stringify(atok)); }); }); @@ -246,7 +246,7 @@ OAuth2Provider.prototype.oauth = function() { res.writeHead(200, {'Content-type': 'application/json'}); - self._createAccessToken(user_id, client_id, function(atok) { + self._createAccessToken(req, user_id, client_id, function(atok) { self.emit('remove_grant', user_id, client_id, code); res.end(JSON.stringify(atok)); @@ -260,14 +260,14 @@ OAuth2Provider.prototype.oauth = function() { }; }; -OAuth2Provider.prototype._createAccessToken = function(user_id, client_id, cb) { +OAuth2Provider.prototype._createAccessToken = function(req, user_id, client_id, cb) { var self = this; - this.emit('create_access_token', user_id, client_id, function(extra_data) { - var atok = self.generateAccessToken(user_id, client_id, extra_data); + this.emit('create_access_token', req, user_id, client_id, function(scope) { + var atok = self.generateAccessToken(user_id, client_id, scope); if(self.listeners('save_access_token').length > 0) - self.emit('save_access_token', user_id, client_id, atok); + self.emit('save_access_token', user_id, client_id, atok, scope); return cb(atok); }); From e8b5ac66e0ee277b2c46d5054c43f207827a2000 Mon Sep 17 00:00:00 2001 From: Bradley Matusiak Date: Sun, 25 Aug 2013 21:45:57 -0400 Subject: [PATCH 6/6] scope updates --- index.js | 58 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/index.js b/index.js index 5d16875..523bc6e 100644 --- a/index.js +++ b/index.js @@ -100,9 +100,10 @@ OAuth2Provider.prototype.oauth = function() { 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) { + redirect_uri = req.query.redirect_uri, + scope = (req.query.scope || "basic"); + + if(!client_id || !redirect_uri || !scope) { res.writeHead(400); return res.end('client_id and redirect_uri required'); } @@ -110,15 +111,16 @@ OAuth2Provider.prototype.oauth = function() { // authorization form will be POSTed to same URL, so we'll have all params var authorize_url = req.url; - self.emit('enforce_login', req, res, authorize_url, function(user_id) { + self.emit('enforce_login', req, res, authorize_url, scope, function(user_id) { // store user_id in an HMAC-protected encrypted query param authorize_url += '&' + querystring.stringify({x_user_id: self.serializer.stringify(user_id)}); - - self.emit('before_authorize_form', req, res,client_id, function() { - self.emit('authorize_form', req, res, client_id, authorize_url); + + //fires when use allows app + self.emit('before_authorize_form', req, res, client_id, scope, function() { + self.emit('authorize_form', req, res, client_id, authorize_url, scope); },function(){ var code = serializer.randomString(128); - self.emit('save_grant', req, client_id, code, function() { + self.emit('save_grant', req, client_id, code, scope, function() { var extras = { code: code, }; @@ -136,7 +138,9 @@ OAuth2Provider.prototype.oauth = function() { }); } else if(req.method == 'POST' && self.options.authorize_uri == uri) { + //fires when use allows app var client_id = (req.query.client_id || req.body.client_id), + scope = (req.query.scope || req.body.scope || "basic"), 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), @@ -154,7 +158,7 @@ OAuth2Provider.prototype.oauth = function() { return res.end('invalid response_type requested'); } if ('allow' in req.body) { - self.emit('after_authorize_form', req, res, client_id,function(){ + self.emit('after_authorize_form', req, res, client_id, scope,function(){ if ('token' == response_type) { var user_id; try { @@ -165,7 +169,7 @@ OAuth2Provider.prototype.oauth = function() { res.writeHead(500); return res.end(e.message); } - self.emit('create_access_token', req, user_id, client_id, function(scope) { + //self.emit('create_access_token', req, user_id, client_id, scope, function(scope) { var atok = self.generateAccessToken(user_id, client_id, scope); if (self.listeners('save_access_token').length > 0) self.emit('save_access_token', user_id, client_id, atok); url += querystring.stringify(atok); @@ -173,11 +177,11 @@ OAuth2Provider.prototype.oauth = function() { Location: url }); res.end(); - }); + //}); } else { var code = serializer.randomString(128); - self.emit('save_grant', req, client_id, code, function() { + self.emit('save_grant', req, client_id, code, scope, function() { var extras = { code: code, }; @@ -202,11 +206,11 @@ OAuth2Provider.prototype.oauth = function() { res.end(); } } else if(req.method == 'POST' && self.options.access_token_uri == uri) { - var client_id = req.body.client_id || req.query.client_id, - client_secret = req.body.client_secret || req.query.client_secret, - redirect_uri = req.body.redirect_uri || req.query.redirect_uri, - code = req.body.code || req.query.code; - + var client_id = req.body.client_id || req.query.client_id, + redirect_uri = req.body.redirect_uri || req.query.redirect_uri, + client_secret = req.body.client_secret || req.query.client_secret, + code = req.body.code || req.query.code; + if(!client_id || !client_secret) { var authorization = parse_authorization(req.headers.authorization); @@ -218,14 +222,15 @@ OAuth2Provider.prototype.oauth = function() { client_id = authorization[0]; client_secret = authorization[1]; } - + + //post auth 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) { + self.emit('client_auth', client_id, client_secret, req.body.username, req.body.password, function(err, user_id, scope) { if(err) { res.writeHead(401); return res.end(err.message); @@ -233,12 +238,13 @@ OAuth2Provider.prototype.oauth = function() { res.writeHead(200, {'Content-type': 'application/json'}); - self._createAccessToken(req, user_id, client_id, function(atok) { + self._createAccessToken(scope, user_id, client_id, function(atok) { + self.emit('remove_grant', user_id, client_id, code, scope); res.end(JSON.stringify(atok)); }); }); } else { - self.emit('lookup_grant', client_id, client_secret, code, function(err, user_id) { + self.emit('lookup_grant', client_id, client_secret, code, function(err, user_id, scope) { if(err) { res.writeHead(400); return res.end(err.message); @@ -246,8 +252,8 @@ OAuth2Provider.prototype.oauth = function() { res.writeHead(200, {'Content-type': 'application/json'}); - self._createAccessToken(req, user_id, client_id, function(atok) { - self.emit('remove_grant', user_id, client_id, code); + self._createAccessToken(scope, user_id, client_id, function(atok) { + self.emit('remove_grant', user_id, client_id, code, scope); res.end(JSON.stringify(atok)); }); @@ -260,17 +266,17 @@ OAuth2Provider.prototype.oauth = function() { }; }; -OAuth2Provider.prototype._createAccessToken = function(req, user_id, client_id, cb) { +OAuth2Provider.prototype._createAccessToken = function(scope, user_id, client_id, cb) { var self = this; - this.emit('create_access_token', req, user_id, client_id, function(scope) { + //this.emit('create_access_token', req, user_id, client_id, function(scope) { var atok = self.generateAccessToken(user_id, client_id, scope); if(self.listeners('save_access_token').length > 0) self.emit('save_access_token', user_id, client_id, atok, scope); return cb(atok); - }); + //}); }; exports.OAuth2Provider = OAuth2Provider;