mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Various resturcturing of the relationship between the different oauth packages
This commit is contained in:
committed by
Nick Martin
parent
6b914593ae
commit
98c364338f
@@ -17,7 +17,7 @@
|
||||
'&redirect_uri=' + Meteor.accounts.facebook._appUrl + '/_oauth/facebook?close' +
|
||||
'&display=' + display + '&scope=' + scope + '&state=' + state;
|
||||
|
||||
Meteor.accounts.oauth.initiateLogin(state, loginUrl, { version: 2 });
|
||||
Meteor.accounts.oauth.initiateLogin(state, loginUrl);
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Meteor.accounts.facebook._secret = secret;
|
||||
};
|
||||
|
||||
Meteor.accounts.oauth.registerService('facebook', {version: 2}, function(query) {
|
||||
Meteor.accounts.oauth.registerService('facebook', 2, function(query) {
|
||||
|
||||
var accessToken = getAccessToken(query);
|
||||
var identity = getIdentity(accessToken);
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
'&redirect_uri=' + Meteor.accounts.google._appUrl + '/_oauth/google?close' +
|
||||
'&state=' + state;
|
||||
|
||||
Meteor.accounts.oauth.initiateLogin(state, loginUrl, { version: 2 });
|
||||
Meteor.accounts.oauth.initiateLogin(state, loginUrl);
|
||||
};
|
||||
|
||||
}) ();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Meteor.accounts.google._secret = secret;
|
||||
};
|
||||
|
||||
Meteor.accounts.oauth.registerService('google', {version: 2}, function(query) {
|
||||
Meteor.accounts.oauth.registerService('google', 2, function(query) {
|
||||
|
||||
var accessToken = getAccessToken(query);
|
||||
var identity = getIdentity(accessToken);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// @param state {String} The OAuth state generated by the client
|
||||
// @param url {String} url to page
|
||||
Meteor.accounts.oauth.initiateLogin = function(state, url, options) {
|
||||
Meteor.accounts.oauth.initiateLogin = function(state, url) {
|
||||
// XXX these dimensions worked well for facebook and google, but
|
||||
// it's sort of weird to have these here. Maybe an optional
|
||||
// argument instead?
|
||||
@@ -12,7 +12,7 @@
|
||||
var checkPopupOpen = setInterval(function() {
|
||||
if (popup.closed) {
|
||||
clearInterval(checkPopupOpen);
|
||||
tryLoginAfterPopupClosed(state, options);
|
||||
tryLoginAfterPopupClosed(state);
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
@@ -20,9 +20,9 @@
|
||||
// Send an OAuth login method to the server. If the user authorized
|
||||
// access in the popup this should log the user in, otherwise
|
||||
// nothing should happen.
|
||||
var tryLoginAfterPopupClosed = function(state, options) {
|
||||
var tryLoginAfterPopupClosed = function(state) {
|
||||
Meteor.apply('login', [
|
||||
{oauth: {version: options.version, state: state}}
|
||||
{oauth: {state: state}}
|
||||
], {wait: true}, function(error, result) {
|
||||
if (error)
|
||||
throw error;
|
||||
@@ -59,4 +59,4 @@
|
||||
newwindow.focus();
|
||||
return newwindow;
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -1,57 +1,99 @@
|
||||
(function () {
|
||||
var connect = __meteor_bootstrap__.require("connect");
|
||||
|
||||
Meteor.accounts.oauth._services = {};
|
||||
|
||||
// Register a handler for an OAuth service. The handler will be called
|
||||
// when we get an incoming http request on /_oauth/{serviceName}. This
|
||||
// handler should use that information to fetch data about the user
|
||||
// logging in.
|
||||
//
|
||||
// @param name {String} e.g. "google", "facebook"
|
||||
// @param version {Number} OAuth version (1 or 2)
|
||||
// @param handleOauthRequest {Function(query)}
|
||||
// - query is an object with the parameters passed in the query string
|
||||
// - return value is:
|
||||
// - {options: (options), extra: (optional extra)} (same as the
|
||||
// arguments to Meteor.accounts.updateOrCreateUser)
|
||||
// - `null` if the user declined to give permissions
|
||||
Meteor.accounts.oauth.registerService = function (name, options, handleOauthRequest) {
|
||||
var oauthAccounts = Meteor.accounts['oauth' + options.version];
|
||||
Meteor.accounts.oauth.registerService = function (name, version, handleOauthRequest) {
|
||||
if (Meteor.accounts.oauth._services[name])
|
||||
throw new Error("Already registered the " + name + " OAuth service");
|
||||
|
||||
if (oauthAccounts._services[name])
|
||||
throw new Error("Already registered the " + name + " OAuth" + options.version + " service");
|
||||
|
||||
oauthAccounts._services[name] = {
|
||||
Meteor.accounts.oauth._services[name] = {
|
||||
serviceName: name,
|
||||
version: version,
|
||||
handleOauthRequest: handleOauthRequest
|
||||
};
|
||||
};
|
||||
|
||||
Meteor.accounts.oauth._setup = function(setupOptions) {
|
||||
var oauthAccounts = Meteor.accounts['oauth' + setupOptions.version];
|
||||
// When we get an incoming OAuth http request we complete the oauth
|
||||
// handshake, account and token setup before responding. The
|
||||
// results are stored in this map which is then read when the login
|
||||
// method is called. Maps state --> return value of `login`
|
||||
//
|
||||
// XXX we should periodically clear old entries
|
||||
Meteor.accounts.oauth._loginResultForState = {};
|
||||
|
||||
// Listen to calls to `login` with an oauth option set
|
||||
Meteor.accounts.registerLoginHandler(function (options) {
|
||||
if (!options.oauth || options.oauth.version !== setupOptions.version)
|
||||
return undefined; // don't handle
|
||||
// Listen to calls to `login` with an oauth option set
|
||||
Meteor.accounts.registerLoginHandler(function (options) {
|
||||
if (!options.oauth)
|
||||
return undefined; // don't handle
|
||||
|
||||
var result = oauthAccounts._loginResultForState[options.oauth.state];
|
||||
if (result === undefined) // not using `!result` since can be null
|
||||
// We weren't notified of the user authorizing the login.
|
||||
return null;
|
||||
else
|
||||
return result;
|
||||
var result = Meteor.accounts.oauth._loginResultForState[options.oauth.state];
|
||||
if (result === undefined) // not using `!result` since can be null
|
||||
// We weren't notified of the user authorizing the login.
|
||||
return null;
|
||||
else
|
||||
return result;
|
||||
});
|
||||
|
||||
// Listen to incoming OAuth http requests
|
||||
__meteor_bootstrap__.app
|
||||
.use(connect.query())
|
||||
.use(function(req, res, next) {
|
||||
// Need to create a Fiber since we're using synchronous http
|
||||
// calls and nothing else is wrapping this in a fiber
|
||||
// automatically
|
||||
Fiber(function () {
|
||||
middleware(req, res, next);
|
||||
}).run();
|
||||
});
|
||||
|
||||
// When we get an incoming OAuth http request we complete the oauth
|
||||
// handshake, account and token setup before responding. The
|
||||
// results are stored in this map which is then read when the login
|
||||
// method is called. Maps state --> return value of `login`
|
||||
//
|
||||
// XXX we should periodically clear old entries
|
||||
oauthAccounts._loginResultForState = {};
|
||||
|
||||
var middleware = function (req, res, next) {
|
||||
var serviceName = requestServiceName(req);
|
||||
if (!serviceName) {
|
||||
// not an oauth request. pass to next middleware.
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
var service = Meteor.accounts.oauth._services[serviceName];
|
||||
|
||||
// Skip everything if there's no service set by the oauth middleware
|
||||
// XXX should we instead throw an error?
|
||||
// XXX we should catch all exceptions here as we do in oauth2_server.js
|
||||
if (!service) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we're configured
|
||||
ensureConfigured(serviceName);
|
||||
|
||||
if (service.version === 1)
|
||||
Meteor.accounts.oauth1._handleRequest(service, req.query, res);
|
||||
else if (service.version === 2)
|
||||
Meteor.accounts.oauth2._handleRequest(service, req.query, res);
|
||||
else
|
||||
throw new Error("Unexpected OAuth version " + service.version);
|
||||
};
|
||||
|
||||
// Handle _oauth paths, gets a bunch of stuff ready for the oauth implementation middleware
|
||||
Meteor.accounts.oauth._requestServiceName = function (req) {
|
||||
//
|
||||
// @returns {String|null} e.g. "facebook", or null if this isn't an
|
||||
// oauth request
|
||||
var requestServiceName = function (req) {
|
||||
|
||||
// req.url will be "/_oauth/<service name>?<action>"
|
||||
var barePath = req.url.substring(0, req.url.indexOf('?'));
|
||||
@@ -63,14 +105,14 @@
|
||||
// Any non-oauth request will continue down the default middlewares
|
||||
// Same goes for service that hasn't been registered
|
||||
if (splitPath[1] !== '_oauth') {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return serviceName;
|
||||
};
|
||||
|
||||
// Make sure we're configured
|
||||
Meteor.accounts.oauth._ensureConfigured = function(serviceName) {
|
||||
var ensureConfigured = function(serviceName) {
|
||||
var service = Meteor.accounts[serviceName];
|
||||
|
||||
_.each(Meteor.accounts[serviceName]._requireConfigs, function(key) {
|
||||
@@ -83,17 +125,6 @@
|
||||
throw new Meteor.accounts.ConfigError("Need to call Meteor.accounts." + serviceName + ".setSecret first");
|
||||
};
|
||||
|
||||
Meteor.accounts.oauth._loadMiddleWare = function(middleware) {
|
||||
__meteor_bootstrap__.app
|
||||
.use(connect.query())
|
||||
.use(function(req, res, next) {
|
||||
// Need to create a Fiber since we're using synchronous http
|
||||
// calls and nothing else is wrapping this in a fiber
|
||||
// automatically
|
||||
Fiber(function () {
|
||||
middleware(req, res, next);
|
||||
}).run();
|
||||
});
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +1,11 @@
|
||||
(function () {
|
||||
var connect = __meteor_bootstrap__.require("connect");
|
||||
|
||||
Meteor.accounts.oauth1._services = {};
|
||||
|
||||
Meteor.accounts.oauth._setup({version: 1});
|
||||
// XXX probably need to catch exceptions here as we do in oauth2_server.js
|
||||
// or put that in oauth_server.js instead
|
||||
|
||||
// connect middleware
|
||||
Meteor.accounts.oauth1._handleRequest = function (req, res, next) {
|
||||
|
||||
var serviceName = Meteor.accounts.oauth._requestServiceName(req);
|
||||
var service = Meteor.accounts.oauth1._services[serviceName];
|
||||
|
||||
// Skip everything if there's no service set by the oauth middleware
|
||||
if (!service) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we're configured
|
||||
Meteor.accounts.oauth._ensureConfigured(serviceName);
|
||||
Meteor.accounts.oauth1._handleRequest = function (service, query, res) {
|
||||
|
||||
// Make sure we prepare the login results before returning.
|
||||
// This way the subsequent call to the `login` method will be
|
||||
@@ -29,10 +16,10 @@
|
||||
|
||||
// If we get here with a callback url we need a request token to
|
||||
// start the logic process
|
||||
if (req.query.callbackUrl) {
|
||||
if (query.callbackUrl) {
|
||||
|
||||
// Get a request token to start auth process
|
||||
oauth.getRequestToken(req.query.callbackUrl);
|
||||
oauth.getRequestToken(query.callbackUrl);
|
||||
|
||||
var redirectUrl = config._urls.authenticate + '?oauth_token=' + oauth.requestToken;
|
||||
res.writeHead(302, {'Location': redirectUrl});
|
||||
@@ -40,53 +27,49 @@
|
||||
|
||||
// If we get here without a callback url we've just
|
||||
// returned from authentication via the oauth provider
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
// XXX Twitter's docs say to check that oauth_token is the
|
||||
// same as the request token received in previous step
|
||||
|
||||
if (!req.query.oauth_token) {
|
||||
// The user didn't authorize access
|
||||
return null;
|
||||
}
|
||||
if (query.oauth_token) {
|
||||
// The user authorized access
|
||||
|
||||
// Get the oauth token for signing requests
|
||||
oauth.getAccessToken(req.query);
|
||||
// Get the oauth token for signing requests
|
||||
oauth.getAccessToken(query);
|
||||
|
||||
// Get or create user id
|
||||
var oauthResult = service.handleOauthRequest(oauth);
|
||||
|
||||
if (oauthResult) { // could be null if user declined permissions
|
||||
// Get or create user id
|
||||
var oauthResult = service.handleOauthRequest(oauth);
|
||||
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});
|
||||
|
||||
|
||||
// Store results to subsequent call to `login`
|
||||
Meteor.accounts.oauth1._loginResultForState[req.query.state] =
|
||||
Meteor.accounts.oauth._loginResultForState[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 =
|
||||
'<html><head><script>window.close()</script></head></html>';
|
||||
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'});
|
||||
}
|
||||
|
||||
// XXX push down to oauth_server.js?
|
||||
|
||||
// We support ?close and ?redirect=URL. Any other query should
|
||||
// just serve a blank page
|
||||
if ('close' in query) { // check with 'in' because we don't set a value
|
||||
// Close the popup window
|
||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
var content =
|
||||
'<html><head><script>window.close()</script></head></html>';
|
||||
res.end(content, 'utf-8');
|
||||
} else if (query.redirect) {
|
||||
res.writeHead(302, {'Location': query.redirect});
|
||||
res.end();
|
||||
} else {
|
||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
res.end('', 'utf-8');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Meteor.accounts.oauth._loadMiddleWare(Meteor.accounts.oauth1._handleRequest);
|
||||
|
||||
})();
|
||||
|
||||
@@ -1,59 +1,58 @@
|
||||
(function () {
|
||||
var connect = __meteor_bootstrap__.require("connect");
|
||||
|
||||
Meteor.accounts.oauth2._services = {};
|
||||
|
||||
Meteor.accounts.oauth._setup({version: 2});
|
||||
|
||||
// connect middleware
|
||||
Meteor.accounts.oauth2._handleRequest = function (req, res, next) {
|
||||
|
||||
var serviceName = Meteor.accounts.oauth._requestServiceName(req);
|
||||
var service = Meteor.accounts.oauth2._services[serviceName];
|
||||
|
||||
// Skip everything if there's no service set by the oauth middleware
|
||||
if (!service) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we're configured
|
||||
Meteor.accounts.oauth._ensureConfigured(serviceName);
|
||||
|
||||
if (req.query.error) {
|
||||
Meteor.accounts.oauth2._handleRequest = function (service, query, res) {
|
||||
if (query.error) {
|
||||
// The user didn't authorize access
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we prepare the login results before returning.
|
||||
// This way the subsequent call to the `login` method will be
|
||||
// immediate.
|
||||
|
||||
// Get or create user id
|
||||
var oauthResult = service.handleOauthRequest(req.query);
|
||||
try {
|
||||
// Get or create user id
|
||||
var oauthResult = service.handleOauthRequest(query);
|
||||
|
||||
if (oauthResult) { // could be null if user declined permissions
|
||||
var userId = Meteor.accounts.updateOrCreateUser(oauthResult.options, oauthResult.extra);
|
||||
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});
|
||||
|
||||
// Store results to subsequent call to `login`
|
||||
Meteor.accounts.oauth2._loginResultForState[req.query.state] =
|
||||
Meteor.accounts.oauth._loginResultForState[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 (query.state && err instanceof Error)
|
||||
Meteor.accounts.oauth._loginResultForState[query.state] = err;
|
||||
|
||||
// also log to the server console, so the developer sees it.
|
||||
Meteor._debug("Exception in oauth2 handler", err);
|
||||
}
|
||||
|
||||
// XXX push down to oauth_server.js?
|
||||
|
||||
// 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
|
||||
if ('close' in query) { // check with 'in' because we don't set a value
|
||||
// Close the popup window
|
||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
var content =
|
||||
'<html><head><script>window.close()</script></head></html>';
|
||||
res.end(content, 'utf-8');
|
||||
} else if (req.query.redirect) {
|
||||
res.writeHead(302, {'Location': req.query.redirect});
|
||||
} else if (query.redirect) {
|
||||
res.writeHead(302, {'Location': query.redirect});
|
||||
res.end();
|
||||
} else {
|
||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
@@ -61,6 +60,4 @@
|
||||
}
|
||||
};
|
||||
|
||||
Meteor.accounts.oauth._loadMiddleWare(Meteor.accounts.oauth2._handleRequest);
|
||||
|
||||
})();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
var callbackUrl = Meteor.accounts.twitter._appUrl + '/_oauth/twitter?close&state=' + state;
|
||||
var url = '/_oauth/twitter/request_token?callbackUrl=' + encodeURIComponent(callbackUrl)
|
||||
|
||||
Meteor.accounts.oauth.initiateLogin(state, url, { version: 1 });
|
||||
Meteor.accounts.oauth.initiateLogin(state, url);
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Meteor.accounts.twitter._secret = secret;
|
||||
};
|
||||
|
||||
Meteor.accounts.oauth.registerService('twitter', {version: 1}, function(oauth) {
|
||||
Meteor.accounts.oauth.registerService('twitter', 1, function(oauth) {
|
||||
|
||||
var identity = oauth.get('https://api.twitter.com/1/account/verify_credentials.json');
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
'&redirect_uri=' + Meteor.accounts.weibo._appUrl + '/_oauth/weibo?close' +
|
||||
'&state=' + state;
|
||||
|
||||
Meteor.accounts.oauth.initiateLogin(state, loginUrl, { version: 2 });
|
||||
Meteor.accounts.oauth.initiateLogin(state, loginUrl);
|
||||
};
|
||||
|
||||
}) ();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Meteor.accounts.weibo._secret = secret;
|
||||
};
|
||||
|
||||
Meteor.accounts.oauth.registerService('weibo', {version: 2}, function(query) {
|
||||
Meteor.accounts.oauth.registerService('weibo', 2, function(query) {
|
||||
|
||||
var accessToken = getAccessToken(query);
|
||||
var identity = getIdentity(accessToken.access_token, parseInt(accessToken.uid, 10));
|
||||
|
||||
Reference in New Issue
Block a user