mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge pull request #2016 from timhaines/multi-server-oauth
OAuth 1 - Move pending request tokens from RAM to mongo.
This commit is contained in:
@@ -10,13 +10,13 @@
|
||||
|
||||
|
||||
// Collection containing pending credentials of oauth credential requests
|
||||
// Has token, credential, and createdAt fields.
|
||||
// Has key, credential, and createdAt fields.
|
||||
Oauth._pendingCredentials = new Meteor.Collection(
|
||||
"meteor_oauth_pendingCredentials", {
|
||||
_preventAutopublish: true
|
||||
});
|
||||
|
||||
Oauth._pendingCredentials._ensureIndex('token', {unique: 1});
|
||||
Oauth._pendingCredentials._ensureIndex('key', {unique: 1});
|
||||
Oauth._pendingCredentials._ensureIndex('createdAt');
|
||||
|
||||
|
||||
@@ -31,18 +31,18 @@ var _cleanStaleResults = function() {
|
||||
var _cleanupHandle = Meteor.setInterval(_cleanStaleResults, 60 * 1000);
|
||||
|
||||
|
||||
// Stores the token and credential in the _pendingCredentials collection
|
||||
// Stores the key and credential in the _pendingCredentials collection
|
||||
// XXX After oauth token encryption is added to Meteor, apply it here too
|
||||
//
|
||||
// @param credentialToken {string}
|
||||
// @param key {string}
|
||||
// @param credential {string} The credential to store
|
||||
//
|
||||
Oauth._storePendingCredential = function (credentialToken, credential) {
|
||||
Oauth._storePendingCredential = function (key, credential) {
|
||||
if (credential instanceof Error)
|
||||
credential = storableError(credential);
|
||||
|
||||
Oauth._pendingCredentials.insert({
|
||||
token: credentialToken,
|
||||
key: key,
|
||||
credential: credential,
|
||||
createdAt: new Date()
|
||||
});
|
||||
@@ -52,12 +52,12 @@ Oauth._storePendingCredential = function (credentialToken, credential) {
|
||||
// Retrieves and removes a credential from the _pendingCredentials collection
|
||||
// XXX After oauth token encryption is added to Meteor, apply it here too
|
||||
//
|
||||
// @param credentialToken {string}
|
||||
// @param key {string}
|
||||
//
|
||||
Oauth._retrievePendingCredential = function (credentialToken) {
|
||||
check(credentialToken, String);
|
||||
Oauth._retrievePendingCredential = function (key) {
|
||||
check(key, String);
|
||||
|
||||
var pendingCredential = Oauth._pendingCredentials.findOne({ token:credentialToken });
|
||||
var pendingCredential = Oauth._pendingCredentials.findOne({ key:key });
|
||||
if (pendingCredential) {
|
||||
Oauth._pendingCredentials.remove({ _id: pendingCredential._id });
|
||||
if (pendingCredential.credential.error)
|
||||
|
||||
79
packages/oauth1/oauth1_pending_request_tokens.js
Normal file
79
packages/oauth1/oauth1_pending_request_tokens.js
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// _pendingRequestTokens are request tokens that have been received
|
||||
// but not yet fully authorized (processed).
|
||||
//
|
||||
// During the oauth1 authorization process, the Meteor App opens
|
||||
// a pop-up, requests a request token from the oauth1 service, and
|
||||
// redirects the browser to the oauth1 service for the user
|
||||
// to grant authorization. The user is then returned to the
|
||||
// Meteor Apps' callback url and the request token is verified.
|
||||
//
|
||||
// When Meteor Apps run on multiple servers, it's possible that
|
||||
// 2 different servers may be used to generate the request token
|
||||
// and to verify it in the callback once the user has authorized.
|
||||
//
|
||||
// For this reason, the _pendingRequestTokens are stored in the database
|
||||
// so they can be shared across Meteor App servers.
|
||||
//
|
||||
|
||||
|
||||
// Collection containing pending request tokens
|
||||
// Has key, requestToken, requestTokenSecret, and createdAt fields.
|
||||
Oauth._pendingRequestTokens = new Meteor.Collection(
|
||||
"meteor_oauth_pendingRequestTokens", {
|
||||
_preventAutopublish: true
|
||||
});
|
||||
|
||||
Oauth._pendingRequestTokens._ensureIndex('key', {unique: 1});
|
||||
Oauth._pendingRequestTokens._ensureIndex('createdAt');
|
||||
|
||||
|
||||
|
||||
// Periodically clear old entries that never got completed
|
||||
var _cleanStaleResults = function() {
|
||||
// Remove request tokens older than 5 minute
|
||||
var timeCutoff = new Date();
|
||||
timeCutoff.setMinutes(timeCutoff.getMinutes() - 5);
|
||||
Oauth._pendingRequestTokens.remove({ createdAt: { $lt: timeCutoff } });
|
||||
};
|
||||
var _cleanupHandle = Meteor.setInterval(_cleanStaleResults, 60 * 1000);
|
||||
|
||||
|
||||
// Stores the key and request token in the _pendingRequestTokens collection
|
||||
// XXX After oauth token encryption is added to Meteor, apply it here too
|
||||
//
|
||||
// @param key {string}
|
||||
// @param requestToken {string}
|
||||
// @param requestTokenSecret {string}
|
||||
//
|
||||
Oauth._storeRequestToken = function (key, requestToken, requestTokenSecret) {
|
||||
Oauth._pendingRequestTokens.insert({
|
||||
key: key,
|
||||
requestToken: requestToken,
|
||||
requestTokenSecret: requestTokenSecret,
|
||||
createdAt: new Date()
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Retrieves and removes a request token from the _pendingRequestTokens collection
|
||||
// Returns an object containing requestToken and requestTokenSecret properties
|
||||
//
|
||||
// XXX After oauth token encryption is added to Meteor, apply it here too
|
||||
//
|
||||
// @param key {string}
|
||||
//
|
||||
Oauth._retrieveRequestToken = function (key) {
|
||||
check(key, String);
|
||||
|
||||
var pendingRequestToken = Oauth._pendingRequestTokens.findOne({ key: key });
|
||||
if (pendingRequestToken) {
|
||||
Oauth._pendingRequestTokens.remove({ _id: pendingRequestToken._id });
|
||||
return {
|
||||
requestToken: pendingRequestToken.requestToken,
|
||||
requestTokenSecret: pendingRequestToken.requestTokenSecret
|
||||
};
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
@@ -1,8 +1,3 @@
|
||||
// A place to store request tokens pending verification
|
||||
var requestTokens = {};
|
||||
|
||||
OAuth1Test = {requestTokens: requestTokens};
|
||||
|
||||
// connect middleware
|
||||
Oauth._requestHandlers['1'] = function (service, query, res) {
|
||||
|
||||
@@ -21,10 +16,10 @@ Oauth._requestHandlers['1'] = function (service, query, res) {
|
||||
oauthBinding.prepareRequestToken(query.requestTokenAndRedirect);
|
||||
|
||||
// Keep track of request token so we can verify it on the next step
|
||||
requestTokens[query.state] = {
|
||||
requestToken: oauthBinding.requestToken,
|
||||
requestTokenSecret: oauthBinding.requestTokenSecret
|
||||
};
|
||||
Oauth._storeRequestToken(query.state,
|
||||
oauthBinding.requestToken,
|
||||
oauthBinding.requestTokenSecret
|
||||
);
|
||||
|
||||
// support for scope/name parameters
|
||||
var redirectUrl = undefined;
|
||||
@@ -42,19 +37,17 @@ Oauth._requestHandlers['1'] = function (service, query, res) {
|
||||
// and close the window to allow the login handler to proceed
|
||||
|
||||
// Get the user's request token so we can verify it and clear it
|
||||
var requestToken = requestTokens[query.state].requestToken;
|
||||
var requestTokenSecret = requestTokens[query.state].requestTokenSecret;
|
||||
delete requestTokens[query.state];
|
||||
var requestTokenInfo = Oauth._retrieveRequestToken(query.state);
|
||||
|
||||
// Verify user authorized access and the oauth_token matches
|
||||
// the requestToken from previous step
|
||||
if (query.oauth_token && query.oauth_token === requestToken) {
|
||||
if (query.oauth_token && query.oauth_token === requestTokenInfo.requestToken) {
|
||||
|
||||
// Prepare the login results before returning. This way the
|
||||
// subsequent call to the `login` method will be immediate.
|
||||
|
||||
// Get the access token for signing requests
|
||||
oauthBinding.prepareAccessToken(query, requestTokenSecret);
|
||||
oauthBinding.prepareAccessToken(query, requestTokenInfo.requestTokenSecret);
|
||||
|
||||
// Run service-specific handler.
|
||||
var oauthResult = service.handleOauthRequest(oauthBinding);
|
||||
|
||||
@@ -40,9 +40,7 @@ Tinytest.add("oauth1 - pendingCredential is stored and can be retrieved", functi
|
||||
});
|
||||
|
||||
// simulate logging in using twitterfoo
|
||||
OAuth1Test.requestTokens[credentialToken] = {
|
||||
requestToken: twitterfooAccessToken
|
||||
};
|
||||
Oauth._storeRequestToken(credentialToken, twitterfooAccessToken);
|
||||
|
||||
var req = {
|
||||
method: "POST",
|
||||
|
||||
@@ -15,6 +15,7 @@ Package.on_use(function (api) {
|
||||
|
||||
api.add_files('oauth1_binding.js', 'server');
|
||||
api.add_files('oauth1_server.js', 'server');
|
||||
api.add_files('oauth1_pending_request_tokens.js', 'server');
|
||||
});
|
||||
|
||||
Package.on_test(function (api) {
|
||||
|
||||
Reference in New Issue
Block a user