From cbce421b982c43ff0bf8d9f9ba15af749199ddf2 Mon Sep 17 00:00:00 2001 From: Avital Oliver Date: Mon, 9 Jul 2012 16:51:16 -0700 Subject: [PATCH] Tests for oauth2 server flow --- .../accounts-oauth2-helper/oauth2_server.js | 106 +++++++++--------- .../accounts-oauth2-helper/oauth2_tests.js | 31 +++++ packages/accounts-oauth2-helper/package.js | 4 + 3 files changed, 90 insertions(+), 51 deletions(-) create mode 100644 packages/accounts-oauth2-helper/oauth2_tests.js diff --git a/packages/accounts-oauth2-helper/oauth2_server.js b/packages/accounts-oauth2-helper/oauth2_server.js index e29c8b592e..62ea444846 100644 --- a/packages/accounts-oauth2-helper/oauth2_server.js +++ b/packages/accounts-oauth2-helper/oauth2_server.js @@ -49,59 +49,63 @@ // XXX we should periodically clear old entries Meteor.accounts.oauth2._loginResultForState = {}; + // connect middleware + Meteor.accounts.oauth2._handleRequest = function (req, res, next) { + // Need to create a Fiber since we're using synchronous http calls + Fiber(function() { + // req.url will be "/_oauth/?" + var barePath = req.url.substring(0, req.url.indexOf('?')); + var splitPath = barePath.split('/'); + + // Any non-oauth request will continue down the default middlewares + if (splitPath[1] !== '_oauth') { + next(); + return; + } + + // Make sure we prepare the login results before returning. + // This way the subsequent call to the `login` method will be + // immediate. + + var serviceName = splitPath[2]; + var service = Meteor.accounts.oauth2._services[serviceName]; + + // Get or create user id + var userInfo = service.handleOauthRequest(req.query); + + var userId = Meteor.accounts.updateOrCreateUser( + userInfo.email, userInfo.userData, serviceName, + userInfo.serviceUserId, userInfo.serviceData); + + // Generate and store a login token for reconnect + // XXX this could go in accounts_server.js instead + var loginToken = Meteor.accounts._loginTokens.insert({userId: userId}); + + // Store results to subsequent call to `login` + Meteor.accounts.oauth2._loginResultForState[req.query.state] = + {token: loginToken, id: userId}; + + // We support ?close and ?redirect=URL. Any other query should + // just serve a blank page + if ('close' in req.query) { // check with 'in' because we don't set a value + // Close the popup window + res.writeHead(200, {'Content-Type': 'text/html'}); + var content = + ''; + res.end(content, 'utf-8'); + } else if (req.query.redirect) { + res.writeHead(302, {'Location': req.query.redirect}); + res.end(); + } else { + res.writeHead(200, {'Content-Type': 'text/html'}); + res.end('', 'utf-8'); + } + }).run(); + }; + // Listen on /_oauth/* __meteor_bootstrap__.app .use(connect.query()) - .use(function (req, res, next) { - // Need to create a Fiber since we're using synchronous http calls - Fiber(function() { - // req.url will be "/_oauth/?" - var barePath = req.url.substring(0, req.url.indexOf('?')); - var splitPath = barePath.split('/'); - - // Any non-oauth request will continue down the default middlewares - if (splitPath[1] !== '_oauth') { - next(); - return; - } - - // Make sure we prepare the login results before returning. - // This way the subsequent call to the `login` method will be - // immediate. - - var serviceName = splitPath[2]; - var service = Meteor.accounts.oauth2._services[serviceName]; - - // Get or create user id - var userInfo = service.handleOauthRequest(req.query); - var userId = Meteor.accounts.updateOrCreateUser( - userInfo.email, userInfo.userData, serviceName, - userInfo.serviceUserId, userInfo.serviceData); - - // Generate and store a login token for reconnect - // XXX this could go in accounts_server.js instead - var loginToken = Meteor.accounts._loginTokens.insert({userId: userId}); - - // Store results to subsequent call to `login` - Meteor.accounts.oauth2._loginResultForState[req.query.state] = - {token: loginToken, id: userId}; - - // We support ?close and ?redirect=URL. Any other query should - // just serve a blank page - if ('close' in req.query) { // check with 'in' because we don't set a value - // Close the popup window - res.writeHead(200, {'Content-Type': 'text/html'}); - var content = - ''; - res.end(content, 'utf-8'); - } else if (req.query.redirect) { - res.writeHead(302, {'Location': req.query.redirect}); - res.end(); - } else { - res.writeHead(200, {'Content-Type': 'text/html'}); - res.end('', 'utf-8'); - } - }).run(); - }); + .use(Meteor.accounts.oauth2._handleRequest); })(); \ No newline at end of file diff --git a/packages/accounts-oauth2-helper/oauth2_tests.js b/packages/accounts-oauth2-helper/oauth2_tests.js new file mode 100644 index 0000000000..3b8d51d76d --- /dev/null +++ b/packages/accounts-oauth2-helper/oauth2_tests.js @@ -0,0 +1,31 @@ +Tinytest.add("oauth2 - loginResultForState is stored", function (test) { + var http = __meteor_bootstrap__.require('http'); + + Meteor.accounts._loginTokens.remove({}); + + // register a fake login service - foobook + Meteor.accounts.oauth2.registerService("foobook", function (query) { + return {email: 'foo@bar.com', userData: {}, + serviceUserId: 1, serviceData: {}}; + }); + + // simulate logging in using foobook + var req = {method: "POST", + url: "/_oauth/foobook?close", + query: {state: "STATE"}}; + Meteor.accounts.oauth2._handleRequest(req, new http.ServerResponse(req)); + + // verify that a user is created + var user = Meteor.users.findOne({emails: 'foo@bar.com'}); + test.equal(user.services.foobook.id, 1); + + // and that that user has a login token + var token = Meteor.accounts._loginTokens.findOne({userId: user._id}); + test.notEqual(token, undefined); + + // and that the login result for that user is prepared + test.equal( + Meteor.accounts.oauth2._loginResultForState['STATE'].id, user._id); + test.equal( + Meteor.accounts.oauth2._loginResultForState['STATE'].token, token._id); +}); diff --git a/packages/accounts-oauth2-helper/package.js b/packages/accounts-oauth2-helper/package.js index c6e6ce5ff9..94a852d156 100644 --- a/packages/accounts-oauth2-helper/package.js +++ b/packages/accounts-oauth2-helper/package.js @@ -10,3 +10,7 @@ Package.on_use(function (api) { api.add_files('oauth2_server.js', 'server'); api.add_files('oauth2_client.js', 'client'); }); + +Package.on_test(function (api) { + api.add_files("oauth2_tests.js", 'server'); +}); \ No newline at end of file