nicer way of handling /_oauth/<service> routes

This commit is contained in:
Mike Bannister
2012-08-01 06:09:41 -04:00
committed by Nick Martin
parent 5f9485d133
commit 9c8fd2958e
4 changed files with 27 additions and 49 deletions

View File

@@ -4,7 +4,7 @@
Meteor.accounts.oauth1._services = {};
// Register a handler for an OAuth1 service. The handler will be called
// when we get an incoming http request on /_oauth1/{serviceName}. This
// when we get an incoming http request on /_oauth/{serviceName}. This
// handler should use that information to fetch data about the user
// logging in.
//
@@ -49,16 +49,17 @@
// connect middleware
Meteor.accounts.oauth1._handleRequest = function (req, res, next) {
// XXX Used _oauth1 so routing can differentiate between oauth1 and 2
// XXX I think the solution is to use a regex that includes the
// applicable providers, e.g. oauth 1 regex would contain (twitter|flickr)
// req.url will be "/_oauth1/<service name>?<action>"
// req.url will be "/_oauth/<service name>?<action>"
var barePath = req.url.substring(0, req.url.indexOf('?'));
var splitPath = barePath.split('/');
// Find service based on url
var serviceName = splitPath[2];
var service = Meteor.accounts.oauth1._services[serviceName];
// Any non-oauth request will continue down the default middlewares
if (splitPath[1] !== '_oauth1') {
// Same goes for service that hasn't been registered
if (splitPath[1] !== '_oauth' || !service) {
next();
return;
}
@@ -67,19 +68,12 @@
// This way the subsequent call to the `login` method will be
// immediate.
var serviceName = splitPath[2];
// XXX check against a list of installed services too
if (!serviceName)
throw new Meteor.accounts.ConfigError("Service could not be found");
// Make sure we're configured
if (!Meteor.accounts[serviceName]._appId || !Meteor.accounts[serviceName]._appUrl)
throw new Meteor.accounts.ConfigError("Need to call Meteor.accounts." + serviceName + ".config first");
if (!Meteor.accounts[serviceName]._secret)
throw new Meteor.accounts.ConfigError("Need to call Meteor.accounts." + serviceName + ".setSecret first");
var service = Meteor.accounts.oauth1._services[serviceName];
var config = Meteor.accounts[serviceName];
var oauth = new OAuth(config);

View File

@@ -18,7 +18,7 @@ Tinytest.add("oauth2 - loginResultForState is stored", function (test) {
// simulate logging in using foobook
var req = {method: "POST",
url: "/_oauth1/foobook?close",
url: "/_oauth/foobook?close",
query: {state: "STATE"}};
Meteor.accounts.oauth1._handleRequest(req, new http.ServerResponse(req));

View File

@@ -56,8 +56,13 @@
var barePath = req.url.substring(0, req.url.indexOf('?'));
var splitPath = barePath.split('/');
// Find service based on url
var serviceName = splitPath[2];
var service = Meteor.accounts.oauth2._services[serviceName];
// Any non-oauth request will continue down the default middlewares
if (splitPath[1] !== '_oauth') {
// Same goes for service that hasn't been registered
if (splitPath[1] !== '_oauth' || !service) {
next();
return;
}
@@ -66,40 +71,19 @@
// 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 oauthResult = service.handleOauthRequest(req.query);
try {
// Get or create user id
var oauthResult = service && service.handleOauthRequest(req.query);
if (oauthResult) { // could be null if user declined permissions
var userId = Meteor.accounts.updateOrCreateUser(oauthResult.options, oauthResult.extra);
// could be null if user declined permissions, or if there was an
// error of some sort.
if (oauthResult && req.query.state) {
var userId = Meteor.accounts.updateOrCreateUser(
oauthResult.options, oauthResult.extra);
// 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});
// 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};
}
} catch (err) {
// if we got thrown an error, save it off, it will get passed to
// the approporiate login call (if any) and reported there.
//
// The other option would be to display it in the popup tab that
// is still open at this point, ignoring the 'close' or 'redirect'
// we were passed. But then the developer wouldn't be able to
// style the error or react to it in any way.
if (req.query.state && err instanceof Error)
Meteor.accounts.oauth2._loginResultForState[req.query.state] = err;
// also log to the server console, so the developer sees it.
Meteor._debug("Exception in oauth2 handler", err);
// 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

View File

@@ -4,8 +4,8 @@
throw new Meteor.accounts.ConfigError("Need to call Meteor.accounts.twitter.config first");
var state = Meteor.uuid();
var callbackUrl = Meteor.accounts.twitter._appUrl + '/_oauth1/twitter?close&state=' + state;
var url = '/_oauth1/twitter/request_token?callbackUrl=' + encodeURIComponent(callbackUrl)
var callbackUrl = Meteor.accounts.twitter._appUrl + '/_oauth/twitter?close&state=' + state;
var url = '/_oauth/twitter/request_token?callbackUrl=' + encodeURIComponent(callbackUrl)
Meteor.accounts.oauth1.initiateLogin(state, url);
};