diff --git a/index.js b/index.js index 88159f0..f1a918b 100644 --- a/index.js +++ b/index.js @@ -34,9 +34,9 @@ 'duomobile.s3-us-west-1.amazonaws.com' ]; - var iframeId = 'duo_iframe', - postAction = '', - postArgument = 'sig_response', + var iframeId, + postAction, + postArgument, host, sigRequest, duoSig, @@ -44,6 +44,26 @@ iframe, submitCallback; + // We use this function instead of setting initial values in the var + // declarations to make sure the initial values and subsequent + // re-initializations are always the same. + initializeStatefulVariables(); + + /** + * Set local variables to whatever they should be before you call init(). + */ + function initializeStatefulVariables() { + iframeId = 'duo_iframe'; + postAction = ''; + postArgument = 'sig_response'; + host = undefined; + sigRequest = undefined; + duoSig = undefined; + appSig = undefined; + iframe = undefined; + submitCallback = undefined; + } + function throwError(message, url) { throw new Error( 'Duo Web SDK error: ' + message + @@ -225,6 +245,12 @@ * submit_callback can be used to prevent the webpage from reloading. */ function init(options) { + // If init() is called more than once we have to reset all the local + // variables to ensure init() will work the same way every time. This + // helps people making single page applications. SPAs may periodically + // remove the iframe and add a new one that has to be initialized. + initializeStatefulVariables(); + if (options) { if (options.host) { host = options.host; @@ -375,7 +401,7 @@ iframe.src = [ 'https://', host, '/frame/web/v1/auth?tx=', duoSig, '&parent=', encodeURIComponent(document.location.href), - '&v=2.6' + '&v=2.7' ].join(''); // listen for the 'message' event diff --git a/package.json b/package.json index 3b9163b..373bbbd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "duo_web_sdk", - "version": "2.6.0", + "version": "2.7.0", "description": "Provides the Duo Web Javascript in an ES6 module format that can be installed via npm and bundled into your web application.", "main": "index.js", "dependencies": {}, diff --git a/test/test-duo-web.js b/test/test-duo-web.js index 277fc5f..b8f3601 100644 --- a/test/test-duo-web.js +++ b/test/test-duo-web.js @@ -9,13 +9,17 @@ describe('Duo Web', function() { /* Dummy sig_request, passes validation. */ var sig_request = 'AUTH|duo_sig:app_sig'; - beforeEach(function() { + function addIframe() { /* Create some document elements that doPostBack expects. */ iframe = document.createElement('iframe'); var form = document.createElement('duo_form'); form.id = 'duo_form'; document.head.appendChild(iframe); iframe.appendChild(form); + } + + beforeEach(function() { + addIframe(); }); it('Should totes work with submitCallback', function() { @@ -34,6 +38,18 @@ describe('Duo Web', function() { assert.isTrue(submitCallbackCalled); }); + it('should include the host in the src', function() { + var host = 'this-is-a-host.example'; + + Duo.init({ + host: host, + iframe: iframe, + sig_request: sig_request + }); + + assert.include(iframe.src, host); + }); + it('should URI encode the parent URL', function() { Duo.init({ host: 'example.com', @@ -48,6 +64,82 @@ describe('Duo Web', function() { iframe.src, 'parent=https://', 'should URI encode the parent href') }) + + describe('Calling init() a second time', function() { + it('Should overwrite the old callback', function() { + var firstSubmitCallbackCalled = false; + var secondSubmitCallbackCalled = false; + + Duo.init({ + host: 'example.com', + iframe: iframe, + sig_request: sig_request, + submit_callback: function() { + firstSubmitCallbackCalled = true; + } + }); + + Duo.init({ + host: 'example.com', + iframe: iframe, + sig_request: sig_request, + submit_callback: function() { + secondSubmitCallbackCalled = true; + } + }); + + Duo._doPostBack("Great Success!"); + + assert.isFalse( + firstSubmitCallbackCalled, + 'did not overwrite the previous callback' + ); + assert.isTrue( + secondSubmitCallbackCalled, + 'did not call the new callback' + ); + }); + + it('Should overwrite the iframe src', function() { + const firstHostName = 'first-host.example'; + const secondHostName = 'second-host.example'; + const thirdHostName = 'third-host.example'; + + // Initialize with one host + Duo.init({ + host: firstHostName, + iframe: iframe, + sig_request: sig_request + }); + + // Initialize with a different host + Duo.init({ + host: secondHostName, + iframe: iframe, + sig_request: sig_request + }); + + assert.include( + iframe.src, secondHostName, + 'did not change the src on the second init' + ); + + + // Create a new iframe and initialze again to make sure the new + // iframe is targeted. + addIframe(); + Duo.init({ + host: thirdHostName, + iframe: iframe, + sig_request: sig_request + }); + + assert.include( + iframe.src, thirdHostName, + 'did not change the src on the third init' + ); + }); + }); }); describe('parseSigRequest', function() {