Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixes mongoose-auth issue: 30. Allows 2nd invalid requests to be made…

… to the Twitter callback path with the same (stale) token. Twitter module now waits for the prior request to update the session, and then the more recent stale-token request responds to the request with this updated session.
  • Loading branch information...
commit 7cb7aa078613242d62a3657bff83b60ef1411bb2 1 parent b41775b
@bnoguchi authored
Showing with 61 additions and 7 deletions.
  1. +61 −7 lib/modules/oauth.js
View
68 lib/modules/oauth.js
@@ -26,6 +26,7 @@ everyModule.submodule('oauth')
, this.consumerSecret()
, '1.0', null, 'HMAC-SHA1');
})
+
.get('entryPath',
'the link a user follows, whereupon you redirect them to the 3rd party OAuth provider dialog - e.g., "/auth/twitter"')
.step('getRequestToken')
@@ -40,12 +41,14 @@ everyModule.submodule('oauth')
.description('sends the user to authorization on the OAuth provider site')
.accepts('res token')
.promises(null)
+
.get('callbackPath',
'the callback path that the 3rd party OAuth provider redirects to after an OAuth authorization result - e.g., "/auth/twitter/callback"')
.step('extractTokenAndVerifier')
.description('extracts the request token and verifier from the url query')
.accepts('req res')
.promises('requestToken verifier')
+ .canBreakTo('handleDuplicateCallbackRequest')
.step('getSession')
.accepts('req')
.promises('session')
@@ -73,6 +76,14 @@ everyModule.submodule('oauth')
.step('sendResponse')
.accepts('res')
.promises(null)
+
+ .stepseq('handleDuplicateCallbackRequest',
+ 'handles the case if you manually click the callback link on Twitter, but Twitter has already sent a redirect request to the callback path with the same token')
+ .step('waitForPriorRequestToWriteSession')
+ .accepts('req res')
+ .promises(null)
+ .step('sendResponse')
+
.getRequestToken( function (req, res) {
// Automatic hostname detection + assignment
@@ -82,7 +93,9 @@ everyModule.submodule('oauth')
var p = this.Promise();
this.oauth.getOAuthRequestToken( function (err, token, tokenSecret, authUrl, params) {
- if (err) return p.fail(err);
+ if (err && !~(err.data.indexOf('Invalid / expired Token'))) {
+ return p.fail(err);
+ }
p.fulfill(token, tokenSecret);
});
return p;
@@ -107,8 +120,23 @@ everyModule.submodule('oauth')
.extractTokenAndVerifier( function (req, res) {
var parsedUrl = url.parse(req.url, true)
, requestToken = parsedUrl.query && parsedUrl.query.oauth_token
- , verifier = parsedUrl.query && parsedUrl.query.oauth_verifier;
- return [requestToken, verifier];
+ , verifier = parsedUrl.query && parsedUrl.query.oauth_verifier
+
+ , sess = req.session
+ , promise
+ , _auth = sess.auth || (sess.auth = {})
+ , mod = _auth[this.name] || (_auth[this.name] = {});
+ if ((mod.token === requestToken) && (mod.verifier === verifier)) {
+ return this.breakTo('handleDuplicateCallbackRequest', req, res);
+ }
+
+ promise = this.Promise();
+ mod.verifier = verifier;
+ sess.save( function (err) {
+ if (err) return promise.fail(err);
+ promise.fulfill(requestToken, verifier);
+ });
+ return promise;
})
.getSession( function(req) {
return req.session;
@@ -119,7 +147,9 @@ everyModule.submodule('oauth')
.getAccessToken( function (reqToken, reqTokenSecret, verifier) {
var promise = this.Promise();
this.oauth.getOAuthAccessToken(reqToken, reqTokenSecret, verifier, function (err, accessToken, accessTokenSecret, params) {
- if (err) return promise.fail(err);
+ if (err && !~(err.data.indexOf('Invalid / expired Token'))) {
+ return promise.fail(err);
+ }
promise.fulfill(accessToken, accessTokenSecret, params);
});
return promise;
@@ -133,21 +163,45 @@ everyModule.submodule('oauth')
};
})
.addToSession( function (sess, auth) {
- var _auth = sess.auth || (sess.auth = {})
- , mod = _auth[this.name] || (_auth[this.name] = {});
+ var promise = this.Promise()
+ , _auth = sess.auth
+ , mod = _auth[this.name];
_auth.loggedIn = true;
_auth.userId || (_auth.userId = auth.user.id);
mod.user = auth.oauthUser;
mod.accessToken = auth.accessToken;
mod.accessTokenSecret = auth.accessTokenSecret;
// this._super() ?
+ sess.save( function (err) {
+ if (err) return promise.fail(err);
+ promise.fulfill();
+ });
+ return promise;
})
- .sendResponse( function (res) {
+ .sendResponse( function (res, data) {
var redirectTo = this.redirectPath();
if (!redirectTo)
throw new Error('You must configure a redirectPath');
res.writeHead(303, {'Location': redirectTo});
res.end();
+ })
+
+ .waitForPriorRequestToWriteSession( function (req, res) {
+ var promise = this.Promise();
+ function check (self, sess, res, promise) {
+ if (sess.auth[self.name].accessToken) {
+ return promise.fulfill();
+ }
+
+ setTimeout(function () {
+ sess.reload( function (err) {
+ if (err) return promise.fail(err);
+ check(self, req.session, res, promise);
+ });
+ }, 100);
+ }
+ check(this, req.session, res, promise);
+ return promise;
});
// Defaults inherited by submodules

0 comments on commit 7cb7aa0

Please sign in to comment.
Something went wrong with that request. Please try again.