diff --git a/.vscode/settings.json b/.vscode/settings.json index 693f93e5..bb96b7c2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ "search.exclude": { "**/node_modules": true, "**/bower_components": true, - "**/build": true + "**/build": true, + "**/docs": true } } \ No newline at end of file diff --git a/src/web-auth/transaction-manager.js b/src/web-auth/transaction-manager.js index 27042c16..b20bc39c 100644 --- a/src/web-auth/transaction-manager.js +++ b/src/web-auth/transaction-manager.js @@ -65,8 +65,12 @@ TransactionManager.prototype.getStoredTransaction = function(state) { var transactionData; transactionData = storage.getItem(this.namespace + state); - storage.removeItem(this.namespace + state); + this.clearTransaction(state); return transactionData; }; +TransactionManager.prototype.clearTransaction = function(state) { + storage.removeItem(this.namespace + state); +}; + module.exports = TransactionManager; diff --git a/src/web-auth/web-message-handler.js b/src/web-auth/web-message-handler.js index 3ae1e577..e87ab917 100644 --- a/src/web-auth/web-message-handler.js +++ b/src/web-auth/web-message-handler.js @@ -22,7 +22,8 @@ function runWebMessageFlow(authorizeUrl, options, callback) { timeoutCallback: function() { callback({ error: 'timeout', - error_description: 'Timeout during executing web_message communication' + error_description: 'Timeout during executing web_message communication', + state: options.state }); } }); @@ -58,10 +59,13 @@ WebMessageHandler.prototype.run = function(options, cb) { ) { var error = err; if (!err && eventData.event.data.response.error) { - error = objectHelper.pick(eventData.event.data.response, ['error', 'error_description']); + error = eventData.event.data.response; + } + if (!error) { + var parsedHash = eventData.event.data.response; + return _this.webAuth.validateAuthenticationResponse(options, parsedHash, cb); } if ( - error && error.error === 'consent_required' && windowHelper.getWindow().location.hostname === 'localhost' ) { @@ -69,11 +73,8 @@ WebMessageHandler.prototype.run = function(options, cb) { "Consent Required. Consent can't be skipped on localhost. Read more here: https://auth0.com/docs/api-auth/user-consent#skipping-consent-for-first-party-clients" ); } - if (error) { - return cb(error); - } - var parsedHash = eventData.event.data.response; - _this.webAuth.validateAuthenticationResponse(options, parsedHash, cb); + _this.webAuth.transactionManager.clearTransaction(error.state); + return cb(objectHelper.pick(error, ['error', 'error_description'])); }); }; diff --git a/test/web-auth/transaction-manager.test.js b/test/web-auth/transaction-manager.test.js index 20784451..83b1b948 100644 --- a/test/web-auth/transaction-manager.test.js +++ b/test/web-auth/transaction-manager.test.js @@ -135,22 +135,34 @@ context('TransactionManager', function() { }); context('getStoredTransaction', function() { beforeEach(function() { - stub(storage, 'removeItem'); stub(storage, 'getItem', function(state) { expect(state).to.be('com.auth0.auth.state'); return { from: 'storage' }; }); + spy(TransactionManager.prototype, 'clearTransaction'); }); afterEach(function() { - storage.removeItem.restore(); storage.getItem.restore(); + TransactionManager.prototype.clearTransaction.restore(); }); it('returns transaction data from storage', function() { var state = this.tm.getStoredTransaction('state'); expect(state).to.be.eql({ from: 'storage' }); }); it('removes data from storage', function() { - var state = this.tm.getStoredTransaction('state'); + this.tm.getStoredTransaction('state'); + expect(TransactionManager.prototype.clearTransaction.firstCall.args[0]).to.be('state'); + }); + }); + context('clearTransaction', function() { + beforeEach(function() { + stub(storage, 'removeItem'); + }); + afterEach(function() { + storage.removeItem.restore(); + }); + it('removes data from storage', function() { + this.tm.clearTransaction('state'); expect(storage.removeItem.calledOnce).to.be(true); expect(storage.removeItem.lastCall.args[0]).to.be('com.auth0.auth.state'); }); diff --git a/test/web-auth/web-auth.test.js b/test/web-auth/web-auth.test.js index 915855a7..d54d6f2c 100644 --- a/test/web-auth/web-auth.test.js +++ b/test/web-auth/web-auth.test.js @@ -2302,6 +2302,7 @@ describe('auth0.WebAuth', function() { stub(TransactionManager.prototype, 'process', function(params) { return Object.assign({}, params, { from: 'transaction-manager' }); }); + spy(TransactionManager.prototype, 'clearTransaction'); stub(windowHelper, 'getOrigin', function() { return 'https://test-origin.com'; }); @@ -2311,6 +2312,7 @@ describe('auth0.WebAuth', function() { }); afterEach(function() { TransactionManager.prototype.process.restore(); + TransactionManager.prototype.clearTransaction.restore(); if (IframeHandler.prototype.init.restore) { IframeHandler.prototype.init.restore(); } @@ -2402,7 +2404,7 @@ describe('auth0.WebAuth', function() { stub(IframeHandler.prototype, 'init', function() { this.timeoutCallback(); }); - this.auth0.checkSession({}, function(err, data) { + this.auth0.checkSession({ state: 'foobar' }, function(err, data) { expect(err).to.be.eql({ error: 'timeout', error_description: 'Timeout during executing web_message communication' @@ -2427,6 +2429,31 @@ describe('auth0.WebAuth', function() { done(); }); }); + it('callback clears transaction on error response', function(done) { + var errorResponse = { + error: 'the-error', + error_description: 'error description', + state: 'foobar' + }; + stub(IframeHandler.prototype, 'init', function() { + this.callback({ event: { data: { response: errorResponse } } }); + }); + this.auth0.checkSession({}, function(err, data) { + expect(TransactionManager.prototype.clearTransaction.firstCall.args[0]).to.be( + errorResponse.state + ); + done(); + }); + }); + it('callback clears transaction on timeout', function(done) { + stub(IframeHandler.prototype, 'init', function() { + this.timeoutCallback(); + }); + this.auth0.checkSession({ state: 'foobar' }, function(err, data) { + expect(TransactionManager.prototype.clearTransaction.firstCall.args[0]).to.be('foobar'); + done(); + }); + }); it('callback writes to console when consent_required + hostname===localhost', function(done) { var errorResponse = { error: 'consent_required'