1
0
mirror of https://github.com/moparisthebest/mail synced 2024-11-26 02:42:17 -05:00

Remove cached OAuth token before reconnect

This commit is contained in:
Tankred Hase 2014-08-05 16:44:39 +02:00
parent eb0002c8d5
commit c4337fba30
6 changed files with 136 additions and 13 deletions

View File

@ -13,7 +13,7 @@
"crypto-lib": "~0.2.1", "crypto-lib": "~0.2.1",
"imap-client": "~0.3.7", "imap-client": "~0.3.7",
"mailreader": "~0.3.5", "mailreader": "~0.3.5",
"pgpmailer": "~0.3.10", "pgpmailer": "~0.3.11",
"pgpbuilder": "~0.3.7", "pgpbuilder": "~0.3.7",
"requirejs": "2.1.14", "requirejs": "2.1.14",
"axe-logger": "~0.0.2", "axe-logger": "~0.0.2",

View File

@ -133,7 +133,8 @@ define(function(require) {
function initClients(credentials) { function initClients(credentials) {
var pgpMailer = new PgpMailer(credentials.smtp, self._pgpbuilder); var pgpMailer = new PgpMailer(credentials.smtp, self._pgpbuilder);
var imapClient = new ImapClient(credentials.imap); var imapClient = new ImapClient(credentials.imap);
imapClient.onError = onImapError; imapClient.onError = onConnectionError;
pgpMailer.onError = onConnectionError;
// certificate update handling // certificate update handling
imapClient.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'imap', self.onConnect, self.onError); imapClient.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'imap', self.onConnect, self.onError);
@ -151,19 +152,19 @@ define(function(require) {
}, callback); }, callback);
} }
function onImapError(error) { function onConnectionError(error) {
axe.debug('IMAP connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : '')); axe.debug('Connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : ''));
setTimeout(function() { setTimeout(function() {
axe.debug('IMAP reconnecting...'); axe.debug('Reconnecting...');
// re-init client modules on error // re-init client modules on error
self.onConnect(function(err) { self.onConnect(function(err) {
if (err) { if (err) {
axe.error('IMAP reconnect attempt failed! ' + (err.errMsg || err.message) + (err.stack ? ('\n' + err.stack) : '')); axe.error('Reconnect attempt failed! ' + (err.errMsg || err.message) + (err.stack ? ('\n' + err.stack) : ''));
return; return;
} }
axe.debug('IMAP reconnect attempt complete.'); axe.debug('Reconnect attempt complete.');
}); });
}, config.reconnectInterval); }, config.reconnectInterval);
} }

View File

@ -256,8 +256,18 @@ define(function(require) {
Auth.prototype.getOAuthToken = function(callback) { Auth.prototype.getOAuthToken = function(callback) {
var self = this; var self = this;
// get a fresh oauth token if (self.oauthToken) {
self._oauth.getOAuthToken(self.emailAddress, function(err, oauthToken) { // removed cached token and get a new one
self._oauth.refreshToken({
emailAddress: self.emailAddress,
oldToken: self.oauthToken
}, onToken);
} else {
// get a fresh oauth token
self._oauth.getOAuthToken(self.emailAddress, onToken);
}
function onToken(err, oauthToken) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
@ -278,7 +288,7 @@ define(function(require) {
self.emailAddress = emailAddress; self.emailAddress = emailAddress;
callback(); callback();
}); });
}); }
}; };
/** /**

View File

@ -5,12 +5,17 @@ define(function() {
this._googleApi = googleApi; this._googleApi = googleApi;
}; };
/**
* Check if chrome.identity api is supported
* @return {Boolean} If is supported
*/
OAuth.prototype.isSupported = function() { OAuth.prototype.isSupported = function() {
return !!(window.chrome && chrome.identity); return !!(window.chrome && chrome.identity);
}; };
/** /**
* Request an OAuth token from chrome for gmail users * Request an OAuth token from chrome for gmail users
* @param {String} emailAddress The user's email address (optional)
*/ */
OAuth.prototype.getOAuthToken = function(emailAddress, callback) { OAuth.prototype.getOAuthToken = function(emailAddress, callback) {
var idOptions = { var idOptions = {
@ -19,7 +24,7 @@ define(function() {
// check which runtime the app is running under // check which runtime the app is running under
chrome.runtime.getPlatformInfo(function(platformInfo) { chrome.runtime.getPlatformInfo(function(platformInfo) {
if ((chrome && chrome.runtime && chrome.runtime.lastError) || !platformInfo) { if (chrome.runtime.lastError || !platformInfo) {
callback(new Error('Error getting chrome platform info!')); callback(new Error('Error getting chrome platform info!'));
return; return;
} }
@ -31,7 +36,7 @@ define(function() {
// get OAuth Token from chrome // get OAuth Token from chrome
chrome.identity.getAuthToken(idOptions, function(token) { chrome.identity.getAuthToken(idOptions, function(token) {
if ((chrome && chrome.runtime && chrome.runtime.lastError) || !token) { if (chrome.runtime.lastError || !token) {
callback({ callback({
errMsg: 'Error fetching an OAuth token for the user!' errMsg: 'Error fetching an OAuth token for the user!'
}); });
@ -43,6 +48,32 @@ define(function() {
}); });
}; };
/**
* Remove an old OAuth token and get a new one.
* @param {String} options.oldToken The old token to be removed
* @param {String} options.emailAddress The user's email address (optional)
*/
OAuth.prototype.refreshToken = function(options, callback) {
var self = this;
if (!options.oldToken) {
callback(new Error('oldToken option not set!'));
return;
}
// remove cached token
chrome.identity.removeCachedAuthToken({
token: options.oldToken
}, function() {
// get a new token
self.getOAuthToken(options.emailAddress, callback);
});
};
/**
* Get email address from google api
* @param {String} token The oauth token
*/
OAuth.prototype.queryEmailAddress = function(token, callback) { OAuth.prototype.queryEmailAddress = function(token, callback) {
if (!token) { if (!token) {
callback({ callback({

View File

@ -146,6 +146,26 @@ define(function(require) {
}); });
describe('#getOAuthToken', function() { describe('#getOAuthToken', function() {
it('should refresh token with known email address', function(done) {
auth.emailAddress = emailAddress;
auth.oauthToken = 'oldToken';
oauthStub.refreshToken.withArgs({
emailAddress: emailAddress,
oldToken: 'oldToken'
}).yieldsAsync(null, oauthToken);
auth.getOAuthToken(function(err) {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.refreshToken.calledOnce).to.be.true;
done();
});
});
it('should fetch token with known email address', function(done) { it('should fetch token with known email address', function(done) {
auth.emailAddress = emailAddress; auth.emailAddress = emailAddress;
oauthStub.getOAuthToken.withArgs(emailAddress).yieldsAsync(null, oauthToken); oauthStub.getOAuthToken.withArgs(emailAddress).yieldsAsync(null, oauthToken);

View File

@ -6,7 +6,7 @@ define(function(require) {
expect = chai.expect; expect = chai.expect;
describe('OAuth unit tests', function() { describe('OAuth unit tests', function() {
var oauth, googleApiStub, identityStub, getPlatformInfoStub, var oauth, googleApiStub, identityStub, getPlatformInfoStub, removeCachedStub,
testEmail = 'test@example.com'; testEmail = 'test@example.com';
beforeEach(function() { beforeEach(function() {
@ -21,6 +21,11 @@ define(function(require) {
} }
identityStub = sinon.stub(window.chrome.identity, 'getAuthToken'); identityStub = sinon.stub(window.chrome.identity, 'getAuthToken');
if (typeof window.chrome.identity.removeCachedAuthToken !== 'function') {
window.chrome.identity.removeCachedAuthToken = function() {};
}
removeCachedStub = sinon.stub(window.chrome.identity, 'removeCachedAuthToken');
window.chrome.runtime = window.chrome.runtime || {}; window.chrome.runtime = window.chrome.runtime || {};
if (typeof window.chrome.runtime.getPlatformInfo !== 'function') { if (typeof window.chrome.runtime.getPlatformInfo !== 'function') {
window.chrome.runtime.getPlatformInfo = function() {}; window.chrome.runtime.getPlatformInfo = function() {};
@ -31,6 +36,7 @@ define(function(require) {
afterEach(function() { afterEach(function() {
identityStub.restore(); identityStub.restore();
getPlatformInfoStub.restore(); getPlatformInfoStub.restore();
removeCachedStub.restore();
}); });
describe('isSupported', function() { describe('isSupported', function() {
@ -39,6 +45,61 @@ define(function(require) {
}); });
}); });
describe('refreshToken', function() {
var getOAuthTokenStub;
beforeEach(function() {
getOAuthTokenStub = sinon.stub(oauth, 'getOAuthToken');
});
afterEach(function() {
getOAuthTokenStub.restore();
});
it('should work', function() {
removeCachedStub.withArgs({
token: 'oldToken'
}).yields();
getOAuthTokenStub.withArgs(testEmail).yields();
oauth.refreshToken({
oldToken: 'oldToken',
emailAddress: testEmail
}, function(err) {
expect(err).to.not.exist;
expect(removeCachedStub.calledOnce).to.be.true;
expect(getOAuthTokenStub.calledOnce).to.be.true;
});
});
it('should work without email', function() {
removeCachedStub.withArgs({
token: 'oldToken'
}).yields();
getOAuthTokenStub.withArgs(undefined).yields();
oauth.refreshToken({
oldToken: 'oldToken',
}, function(err) {
expect(err).to.not.exist;
expect(removeCachedStub.calledOnce).to.be.true;
expect(getOAuthTokenStub.calledOnce).to.be.true;
expect(getOAuthTokenStub.calledWith(undefined)).to.be.true;
});
});
it('should fail without all options', function() {
oauth.refreshToken({
emailAddress: testEmail
}, function(err) {
expect(err).to.exist;
expect(removeCachedStub.called).to.be.false;
expect(getOAuthTokenStub.called).to.be.false;
});
});
});
describe('getOAuthToken', function() { describe('getOAuthToken', function() {
it('should work for empty emailAddress', function(done) { it('should work for empty emailAddress', function(done) {
getPlatformInfoStub.yields({ getPlatformInfoStub.yields({