@duosecurity/duo_web
Advanced tools
Comparing version 1.0.4 to 1.0.5
@@ -8,5 +8,4 @@ { | ||
"rules": { | ||
"camelcase": "off", | ||
"node/no-deprecated-api": "off" | ||
"camelcase": "off" | ||
} | ||
} |
@@ -14,2 +14,2 @@ Demonstration of a simple Nodejs web server with Duo authentication. | ||
# Usage: # | ||
Visit the root URL with a 'user' argument, e.g. 'http://localhost:8080/?user=myname'. | ||
Visit the root URL with a 'user' argument, e.g. 'http://localhost:8080/?username=myname'. |
55
duo.js
@@ -19,7 +19,26 @@ var crypto = require('crypto') | ||
exports.ERR_USER = ERR_USER | ||
exports.ERR_IKEY = ERR_IKEY | ||
exports.ERR_SKEY = ERR_SKEY | ||
exports.ERR_AKEY = ERR_AKEY | ||
class UsernameError extends Error { | ||
constructor (message) { | ||
super(message) | ||
this.name = this.constructor.name | ||
Error.captureStackTrace(this, this.constructor) | ||
} | ||
} | ||
class IkeyError extends Error { | ||
constructor (message) { | ||
super(message) | ||
this.name = this.constructor.name | ||
Error.captureStackTrace(this, this.constructor) | ||
} | ||
} | ||
class AkeyError extends Error { | ||
constructor (message) { | ||
super(message) | ||
this.name = this.constructor.name | ||
Error.captureStackTrace(this, this.constructor) | ||
} | ||
} | ||
/** | ||
@@ -42,7 +61,3 @@ * @function sign a value | ||
/** | ||
* Move to Buffer.from and remove no-deprecated-api | ||
* lint exception when we remove Node v4 support | ||
*/ | ||
var b64 = new Buffer(val).toString('base64') | ||
var b64 = Buffer.from(val).toString('base64') | ||
var cookie = prefix + '|' + b64 | ||
@@ -92,7 +107,3 @@ | ||
/** | ||
* Move to Buffer.from and remove no-deprecated-api | ||
* lint exception when we remove Node v4 support | ||
*/ | ||
var cookie_parts = new Buffer(u_b64, 'base64').toString('utf8').split('|') | ||
var cookie_parts = Buffer.from(u_b64, 'base64').toString('utf8').split('|') | ||
if (cookie_parts.length !== 3) { | ||
@@ -129,3 +140,3 @@ return null | ||
*/ | ||
exports.sign_request = function (ikey, skey, akey, username) { | ||
var sign_request = function (ikey, skey, akey, username) { | ||
if (!username || username.length < 1) { | ||
@@ -168,3 +179,3 @@ return ERR_USER | ||
*/ | ||
exports.verify_response = function (ikey, skey, akey, sig_response) { | ||
var verify_response = function (ikey, skey, akey, sig_response) { | ||
var parts = sig_response.split(':') | ||
@@ -186,1 +197,13 @@ if (parts.length !== 2) { | ||
} | ||
module.exports = { | ||
'sign_request': sign_request, | ||
'verify_response': verify_response, | ||
'ERR_USER': ERR_USER, | ||
'ERR_IKEY': ERR_IKEY, | ||
'ERR_AKEY': ERR_AKEY, | ||
'ERR_SKEY': ERR_SKEY, | ||
'UsernameError': UsernameError, | ||
'IkeyError': IkeyError, | ||
'AkeyError': AkeyError | ||
} |
/** | ||
* Duo Web SDK v2 | ||
* Copyright 2017, Duo Security | ||
* Copyright 2019, Duo Security | ||
*/ | ||
@@ -37,5 +37,4 @@ | ||
var iframeId = 'duo_iframe', | ||
postAction = '', | ||
postArgument = 'sig_response', | ||
var postAction, | ||
postArgument, | ||
host, | ||
@@ -48,3 +47,26 @@ sigRequest, | ||
function throwError(message, url) { | ||
// 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() { | ||
postAction = ''; | ||
postArgument = 'sig_response'; | ||
host = undefined; | ||
sigRequest = undefined; | ||
duoSig = undefined; | ||
appSig = undefined; | ||
iframe = undefined; | ||
submitCallback = undefined; | ||
} | ||
function throwError(message, givenUrl) { | ||
var url = ( | ||
givenUrl || | ||
'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe' | ||
); | ||
throw new Error( | ||
@@ -123,4 +145,3 @@ 'Duo Web SDK error: ' + message + | ||
'Duo was given a bad token. This might indicate a configuration ' + | ||
'problem with one of Duo\'s client libraries.', | ||
'https://www.duosecurity.com/docs/duoweb#first-steps' | ||
'problem with one of Duo\'s client libraries.' | ||
); | ||
@@ -144,26 +165,2 @@ } | ||
/** | ||
* This function is set up to run when the DOM is ready, if the iframe was | ||
* not available during `init`. | ||
*/ | ||
function onDOMReady() { | ||
iframe = document.getElementById(iframeId); | ||
if (!iframe) { | ||
throw new Error( | ||
'This page does not contain an iframe for Duo to use.' + | ||
'Add an element like <iframe id="duo_iframe"></iframe> ' + | ||
'to this page. ' + | ||
'See https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe ' + | ||
'for more information.' | ||
); | ||
} | ||
// we've got an iframe, away we go! | ||
ready(); | ||
// always clean up after yourself | ||
offReady(onDOMReady); | ||
} | ||
/** | ||
* Validate that a MessageEvent came from the Duo service, and that it | ||
@@ -211,3 +208,3 @@ * is a properly formatted payload. | ||
* Example using `data-` attributes: | ||
* ``` | ||
* ```html | ||
* <iframe id="duo_iframe" | ||
@@ -222,13 +219,52 @@ * data-host="api-main.duo.test" | ||
* | ||
* Some browsers (especially embedded browsers) don't like it when the Duo | ||
* Web SDK changes the `src` attribute on the iframe. To prevent this, there | ||
* is an alternative way to use the Duo Web SDK: | ||
* | ||
* Add a div (or any other container element) instead of an iframe to the | ||
* DOM with an id of "duo_iframe", or pass that element to the | ||
* `iframeContainer` parameter of `Duo.init`. An iframe will be created and | ||
* inserted into that container element, preventing `src` change related | ||
* bugs. WARNING: All other elements in the container will be deleted. | ||
* | ||
* The `iframeAttributes` parameter of `Duo.init` is available to set any | ||
* attributes on the inserted iframe if the Duo Web SDK is inserting the | ||
* iframe. For details, see the parameter documentation below. | ||
* | ||
* @param {Object} options | ||
* @param {String} options.iframe The iframe, or id of an iframe to set up | ||
* @param {String} options.host Hostname | ||
* @param {String} options.sig_request Request token | ||
* @param {String} [options.post_action=''] URL to POST back to after successful auth | ||
* @param {String} [options.post_argument='sig_response'] Parameter name to use for response token | ||
* @param {Function} [options.submit_callback] If provided, duo will not submit the form instead execute | ||
* the callback function with reference to the "duo_form" form object | ||
* submit_callback can be used to prevent the webpage from reloading. | ||
* @param {String} options.host - Hostname for the Duo Prompt. | ||
* @param {String} options.sig_request - Request token. | ||
* @param {String|HTMLElement} [options.iframe] - The iframe, or id of an | ||
* iframe that will be used for the Duo Prompt. If you don't provide | ||
* this or the `iframeContainer` parameter the Duo Web SDK will default | ||
* to using whatever element has an id of "duo_iframe". | ||
* @param {String|HTMLElement} [options.iframeContainer] - The element you | ||
* want the Duo Prompt inserted into, or the id of that element. | ||
* Anything inside this element will be deleted and replaced with an | ||
* iframe hosting the Duo prompt. If you don't provide this or the | ||
* `iframe` parameter the Duo Web SDK will default to using whatever | ||
* element has an id of "duo_iframe". | ||
* @param {Object} [options.iframeAttributes] - Object with names and | ||
* values coresponding to attributes you want added to the Duo Prompt | ||
* iframe, like `title`, `width` and `allow`. WARNING: this parameter | ||
* only works if you use the `iframeContainer` parameter or add an id | ||
* of "duo_iframe" to an element that isn't an iframe. If you have | ||
* added an iframe to the DOM yourself, you should set those attributes | ||
* directly on the iframe. | ||
* @param {String} [options.post_action=''] - URL to POST back to after a | ||
* successful auth. | ||
* @param {String} [options.post_argument='sig_response'] - Parameter name | ||
* to use for response token. | ||
* @param {Function} [options.submit_callback] - If provided, the Duo Web | ||
* SDK will not submit the form. Instead it will execute this callback | ||
* function passing in a reference to the "duo_form" form object. | ||
* `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) { | ||
@@ -251,10 +287,2 @@ if (options.host) { | ||
if (options.iframe) { | ||
if (options.iframe.tagName) { | ||
iframe = options.iframe; | ||
} else if (typeof options.iframe === 'string') { | ||
iframeId = options.iframe; | ||
} | ||
} | ||
if (typeof options.submit_callback === 'function') { | ||
@@ -265,16 +293,10 @@ submitCallback = options.submit_callback; | ||
// if we were given an iframe, no need to wait for the rest of the DOM | ||
if (iframe) { | ||
ready(); | ||
var promptElement = getPromptElement(options); | ||
if (promptElement) { | ||
// If we can get the element that will host the prompt, set it. | ||
ready(promptElement, options.iframeAttributes || {}); | ||
} else { | ||
// try to find the iframe in the DOM | ||
iframe = document.getElementById(iframeId); | ||
// iframe is in the DOM, away we go! | ||
if (iframe) { | ||
ready(); | ||
} else { | ||
// wait until the DOM is ready, then try again | ||
onReady(onDOMReady); | ||
} | ||
// If the element that will host the prompt isn't available yet, set | ||
// it up after the DOM finishes loading. | ||
asyncReady(options); | ||
} | ||
@@ -287,2 +309,91 @@ | ||
/** | ||
* Given the options from init(), get the iframe or iframe container that | ||
* should be used for the Duo Prompt. Returns `null` if nothing was found. | ||
*/ | ||
function getPromptElement(options) { | ||
var result; | ||
if (options.iframe && options.iframeContainer) { | ||
throwError( | ||
'Passing both `iframe` and `iframeContainer` arguments at the' + | ||
' same time is not allowed.' | ||
); | ||
} else if (options.iframe) { | ||
// If we are getting an iframe, try to get it and raise if the | ||
// element we find is NOT an iframe. | ||
result = getUserDefinedElement(options.iframe); | ||
validateIframe(result); | ||
} else if (options.iframeContainer) { | ||
result = getUserDefinedElement(options.iframeContainer); | ||
validateIframeContainer(result); | ||
} else { | ||
result = document.getElementById('duo_iframe'); | ||
} | ||
return result; | ||
} | ||
/** | ||
* When given an HTMLElement, return it. When given a string, get an element | ||
* with that id, else return null. | ||
*/ | ||
function getUserDefinedElement(object) { | ||
if (object.tagName) { | ||
return object; | ||
} else if (typeof object == 'string') { | ||
return document.getElementById(object); | ||
} | ||
return null; | ||
} | ||
/** | ||
* Check if the given thing is an iframe. | ||
*/ | ||
function isIframe(element) { | ||
return ( | ||
element && | ||
element.tagName && | ||
element.tagName.toLowerCase() === 'iframe' | ||
); | ||
} | ||
/** | ||
* Throw an error if we are given an element that is NOT an iframe. | ||
*/ | ||
function validateIframe(element) { | ||
if (element && !isIframe(element)) { | ||
throwError( | ||
'`iframe` only accepts an iframe element or the id of an' + | ||
' iframe. To use a non-iframe element, use the' + | ||
' `iframeContainer` argument.' | ||
); | ||
} | ||
} | ||
/** | ||
* Throw an error if we are given an element that IS an iframe instead of an | ||
* element that we can insert an iframe into. | ||
*/ | ||
function validateIframeContainer(element) { | ||
if (element && isIframe(element)) { | ||
throwError( | ||
'`iframeContainer` only accepts a non-iframe element or the' + | ||
' id of a non-iframe. To use a non-iframe element, use the' + | ||
' `iframeContainer` argument on Duo.init().' | ||
); | ||
} | ||
} | ||
/** | ||
* Generate the URL that goes to the Duo Prompt. | ||
*/ | ||
function generateIframeSrc() { | ||
return [ | ||
'https://', host, '/frame/web/v1/auth?tx=', duoSig, | ||
'&parent=', encodeURIComponent(document.location.href), | ||
'&v=2.8' | ||
].join(''); | ||
} | ||
/** | ||
* This function is called when a message was received from another domain | ||
@@ -344,7 +455,30 @@ * using the `postMessage` API. Check that the event came from the Duo | ||
/** | ||
* Register a callback to call ready() after the DOM has loaded. | ||
*/ | ||
function asyncReady(options) { | ||
var callback = function() { | ||
var promptElement = getPromptElement(options); | ||
if (!promptElement) { | ||
throwError( | ||
'This page does not contain an iframe for Duo to use.' + | ||
' Add an element like' + | ||
' <iframe id="duo_iframe"></iframe> to this page.' | ||
); | ||
} | ||
ready(promptElement, options.iframeAttributes || {}); | ||
// Always clean up after yourself. | ||
offReady(callback) | ||
}; | ||
onReady(callback); | ||
} | ||
/** | ||
* Point the iframe at Duo, then wait for it to postMessage back to us. | ||
*/ | ||
function ready() { | ||
function ready(promptElement, iframeAttributes) { | ||
if (!host) { | ||
host = getDataAttribute(iframe, 'host'); | ||
host = getDataAttribute(promptElement, 'host'); | ||
@@ -355,4 +489,3 @@ if (!host) { | ||
'a `host` parameter to Duo.init, or through the `data-host` ' + | ||
'attribute on the iframe element.', | ||
'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe' | ||
'attribute on the iframe element.' | ||
); | ||
@@ -363,3 +496,3 @@ } | ||
if (!duoSig || !appSig) { | ||
parseSigRequest(getDataAttribute(iframe, 'sigRequest')); | ||
parseSigRequest(getDataAttribute(promptElement, 'sigRequest')); | ||
@@ -370,4 +503,3 @@ if (!duoSig || !appSig) { | ||
'`sig_request` parameter to Duo.init, or use the ' + | ||
'`data-sig-request` attribute on the iframe element.', | ||
'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe' | ||
'`data-sig-request` attribute on the iframe element.' | ||
); | ||
@@ -380,16 +512,32 @@ } | ||
if (postAction === '') { | ||
postAction = getDataAttribute(iframe, 'postAction') || postAction; | ||
postAction = getDataAttribute(promptElement, 'postAction') || postAction; | ||
} | ||
if (postArgument === 'sig_response') { | ||
postArgument = getDataAttribute(iframe, 'postArgument') || postArgument; | ||
postArgument = getDataAttribute(promptElement, 'postArgument') || postArgument; | ||
} | ||
// point the iframe at Duo | ||
iframe.src = [ | ||
'https://', host, '/frame/web/v1/auth?tx=', duoSig, | ||
'&parent=', encodeURIComponent(document.location.href), | ||
'&v=2.6' | ||
].join(''); | ||
if (isIframe(promptElement)) { | ||
iframe = promptElement; | ||
iframe.src = generateIframeSrc(); | ||
} else { | ||
// If given a container to put an iframe in, clean out any children | ||
// child elements in case `init()` was called more than once. | ||
while (promptElement.firstChild) { | ||
// We call `removeChild()` instead of doing `innerHTML = ""` | ||
// to make sure we unbind any events. | ||
promptElement.removeChild(promptElement.firstChild) | ||
} | ||
iframe = document.createElement('iframe'); | ||
// Set the src and all other attributes on the new iframe. | ||
iframeAttributes['src'] = generateIframeSrc(); | ||
for (var name in iframeAttributes) { | ||
iframe.setAttribute(name, iframeAttributes[name]); | ||
} | ||
promptElement.appendChild(iframe); | ||
} | ||
// listen for the 'message' event | ||
@@ -396,0 +544,0 @@ onMessage(onReceivedMessage); |
@@ -1,1 +0,1 @@ | ||
(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else if(typeof module==="object"&&module.exports){module.exports=t()}else{var o=t();o._onReady(o.init);e.Duo=o}})(this,function(){var e=/^(?:AUTH|ENROLL)+\|[A-Za-z0-9\+\/=]+\|[A-Za-z0-9\+\/=]+$/;var t=/^ERR\|[\w\s\.\(\)]+$/;var o=/^DUO_OPEN_WINDOW\|/;var n=["duo.com","duosecurity.com","duomobile.s3-us-west-1.amazonaws.com"];var i="duo_iframe",a="",r="sig_response",s,u,f,d,m,c;function l(e,t){throw new Error("Duo Web SDK error: "+e+(t?"\n"+"See "+t+" for more information":""))}function h(e){return e.replace(/([a-z])([A-Z])/,"$1-$2").toLowerCase()}function p(e,t){if("dataset"in e){return e.dataset[t]}else{return e.getAttribute("data-"+h(t))}}function g(e,t,o,n){if("addEventListener"in window){e.addEventListener(t,n,false)}else{e.attachEvent(o,n)}}function w(e,t,o,n){if("removeEventListener"in window){e.removeEventListener(t,n,false)}else{e.detachEvent(o,n)}}function v(e){g(document,"DOMContentLoaded","onreadystatechange",e)}function _(e){w(document,"DOMContentLoaded","onreadystatechange",e)}function b(e){g(window,"message","onmessage",e)}function E(e){w(window,"message","onmessage",e)}function y(e){if(!e){return}if(e.indexOf("ERR|")===0){l(e.split("|")[1])}if(e.indexOf(":")===-1||e.split(":").length!==2){l("Duo was given a bad token. This might indicate a configuration "+"problem with one of Duo's client libraries.","https://www.duosecurity.com/docs/duoweb#first-steps")}var t=e.split(":");u=e;f=t[0];d=t[1];return{sigRequest:e,duoSig:t[0],appSig:t[1]}}function D(){m=document.getElementById(i);if(!m){throw new Error("This page does not contain an iframe for Duo to use."+'Add an element like <iframe id="duo_iframe"></iframe> '+"to this page. "+"See https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe "+"for more information.")}q();_(D)}function O(n){return Boolean(n.origin==="https://"+s&&typeof n.data==="string"&&(n.data.match(e)||n.data.match(t)||n.data.match(o)))}function R(e){if(e){if(e.host){s=e.host}if(e.sig_request){y(e.sig_request)}if(e.post_action){a=e.post_action}if(e.post_argument){r=e.post_argument}if(e.iframe){if(e.iframe.tagName){m=e.iframe}else if(typeof e.iframe==="string"){i=e.iframe}}if(typeof e.submit_callback==="function"){c=e.submit_callback}}if(m){q()}else{m=document.getElementById(i);if(m){q()}else{v(D)}}_(R)}function A(e){if(O(e)){if(e.data.match(o)){var t=e.data.substring("DUO_OPEN_WINDOW|".length);if(L(t)){window.open(t,"_self")}}else{B(e.data);E(A)}}}function L(e){if(!e){return false}var t=document.createElement("a");t.href=e;if(t.protocol==="duotrustedendpoints:"){return true}else if(t.protocol!=="https:"){return false}for(var o=0;o<n.length;o++){if(t.hostname.endsWith("."+n[o])||t.hostname===n[o]){return true}}return false}function q(){if(!s){s=p(m,"host");if(!s){l("No API hostname is given for Duo to use. Be sure to pass "+"a `host` parameter to Duo.init, or through the `data-host` "+"attribute on the iframe element.","https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe")}}if(!f||!d){y(p(m,"sigRequest"));if(!f||!d){l("No valid signed request is given. Be sure to give the "+"`sig_request` parameter to Duo.init, or use the "+"`data-sig-request` attribute on the iframe element.","https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe")}}if(a===""){a=p(m,"postAction")||a}if(r==="sig_response"){r=p(m,"postArgument")||r}m.src=["https://",s,"/frame/web/v1/auth?tx=",f,"&parent=",encodeURIComponent(document.location.href),"&v=2.6"].join("");b(A)}function B(e){var t=document.createElement("input");t.type="hidden";t.name=r;t.value=e+":"+d;var o=document.getElementById("duo_form");if(!o){o=document.createElement("form");m.parentElement.insertBefore(o,m.nextSibling)}o.method="POST";o.action=a;o.appendChild(t);if(typeof c==="function"){c.call(null,o)}else{o.submit()}}return{init:R,_onReady:v,_parseSigRequest:y,_isDuoMessage:O,_doPostBack:B}}); | ||
!function(e,t){if("function"==typeof define&&define.amd)define([],t);else if("object"==typeof module&&module.exports)module.exports=t();else{var n=t();n._onReady(n.init),e.Duo=n}}(this,function(){var i,a,r,s,u,f,m,t=/^(?:AUTH|ENROLL)+\|[A-Za-z0-9\+\/=]+\|[A-Za-z0-9\+\/=]+$/,n=/^ERR\|[\w\s\.\(\)]+$/,d=/^DUO_OPEN_WINDOW\|/,c=["duo.com","duosecurity.com","duomobile.s3-us-west-1.amazonaws.com"];function l(){i="",a="sig_response",r=undefined,undefined,s=undefined,u=undefined,f=undefined,m=undefined}function h(e,t){var n=t||"https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe";throw new Error("Duo Web SDK error: "+e+(n?"\nSee "+n+" for more information":""))}function g(e,t){return"dataset"in e?e.dataset[t]:e.getAttribute("data-"+function n(e){return e.replace(/([a-z])([A-Z])/,"$1-$2").toLowerCase()}(t))}function p(e,t,n,o){"addEventListener"in window?e.addEventListener(t,o,!1):e.attachEvent(n,o)}function v(e,t,n,o){"removeEventListener"in window?e.removeEventListener(t,o,!1):e.detachEvent(n,o)}function w(e){p(document,"DOMContentLoaded","onreadystatechange",e)}function _(e){v(document,"DOMContentLoaded","onreadystatechange",e)}function b(e){if(e){0===e.indexOf("ERR|")&&h(e.split("|")[1]),-1!==e.indexOf(":")&&2===e.split(":").length||h("Duo was given a bad token. This might indicate a configuration problem with one of Duo's client libraries.");var t=e.split(":");return e,s=t[0],u=t[1],{sigRequest:e,duoSig:t[0],appSig:t[1]}}}function E(e){return Boolean(e.origin==="https://"+r&&"string"==typeof e.data&&(e.data.match(t)||e.data.match(n)||e.data.match(d)))}function y(e){var t;return e.iframe&&e.iframeContainer?h("Passing both `iframe` and `iframeContainer` arguments at the same time is not allowed."):e.iframe?function n(e){e&&!D(e)&&h("`iframe` only accepts an iframe element or the id of an iframe. To use a non-iframe element, use the `iframeContainer` argument.")}(t=C(e.iframe)):e.iframeContainer?function o(e){e&&D(e)&&h("`iframeContainer` only accepts a non-iframe element or the id of a non-iframe. To use a non-iframe element, use the `iframeContainer` argument on Duo.init().")}(t=C(e.iframeContainer)):t=document.getElementById("duo_iframe"),t}function C(e){return e.tagName?e:"string"==typeof e?document.getElementById(e):null}function D(e){return e&&e.tagName&&"iframe"===e.tagName.toLowerCase()}function A(){return["https://",r,"/frame/web/v1/auth?tx=",s,"&parent=",encodeURIComponent(document.location.href),"&v=2.8"].join("")}function O(e){if(E(e))if(e.data.match(d)){var t=e.data.substring("DUO_OPEN_WINDOW|".length);(function o(e){if(!e)return!1;var t=document.createElement("a");{if(t.href=e,"duotrustedendpoints:"===t.protocol)return!0;if("https:"!==t.protocol)return!1}for(var n=0;n<c.length;n++)if(t.hostname.endsWith("."+c[n])||t.hostname===c[n])return!0;return!1})(t)&&window.open(t,"_self")}else L(e.data),function n(e){v(window,"message","onmessage",e)}(O)}function R(e,t){if(r||(r=g(e,"host"))||h("No API hostname is given for Duo to use. Be sure to pass a `host` parameter to Duo.init, or through the `data-host` attribute on the iframe element."),s&&u||(b(g(e,"sigRequest")),s&&u||h("No valid signed request is given. Be sure to give the `sig_request` parameter to Duo.init, or use the `data-sig-request` attribute on the iframe element.")),""===i&&(i=g(e,"postAction")||i),"sig_response"===a&&(a=g(e,"postArgument")||a),D(e))(f=e).src=A();else{for(;e.firstChild;)e.removeChild(e.firstChild);for(var n in f=document.createElement("iframe"),t.src=A(),t)f.setAttribute(n,t[n]);e.appendChild(f)}!function o(e){p(window,"message","onmessage",e)}(O)}function L(e){var t=document.createElement("input");t.type="hidden",t.name=a,t.value=e+":"+u;var n=document.getElementById("duo_form");n||(n=document.createElement("form"),f.parentElement.insertBefore(n,f.nextSibling)),n.method="POST",n.action=i,n.appendChild(t),"function"==typeof m?m.call(null,n):n.submit()}return l(),{init:function N(e){l(),e&&(e.host&&(r=e.host),e.sig_request&&b(e.sig_request),e.post_action&&(i=e.post_action),e.post_argument&&(a=e.post_argument),"function"==typeof e.submit_callback&&(m=e.submit_callback));var t=y(e);t?R(t,e.iframeAttributes||{}):function o(t){var n=function(){var e=y(t);e||h('This page does not contain an iframe for Duo to use. Add an element like <iframe id="duo_iframe"></iframe> to this page.'),R(e,t.iframeAttributes||{}),_(n)};w(n)}(e),_(N)},_onReady:w,_parseSigRequest:b,_isDuoMessage:E,_doPostBack:L}}); |
{ | ||
"name": "@duosecurity/duo_web", | ||
"version": "1.0.4", | ||
"version": "1.0.5", | ||
"license": "BSD-3-Clause", | ||
@@ -13,3 +13,3 @@ "description": "Duo two-factor authentication for Node.js web applications", | ||
"scripts": { | ||
"test": "nodeunit tests", | ||
"test": "./node_modules/mocha/bin/mocha ./tests/*", | ||
"lint": "eslint duo.js index.js tests/ demos/" | ||
@@ -20,6 +20,7 @@ }, | ||
"eslint-config-standard": "^11.0.0", | ||
"eslint-plugin-import": "^2.8.0", | ||
"eslint-plugin-node": "^5.2.1", | ||
"eslint-plugin-promise": "^3.7.0", | ||
"eslint-plugin-standard": "^3.0.1", | ||
"eslint-plugin-import": "^2.8.0", | ||
"eslint-plugin-node": "^5.2.1", | ||
"mocha": "^5.2.0", | ||
"nodeunit": ">= 0.8.6" | ||
@@ -26,0 +27,0 @@ }, |
@@ -24,3 +24,3 @@ # Overview | ||
``` | ||
$ npm install global @duosecurity/duo_web | ||
$ npm install --global @duosecurity/duo_web | ||
``` | ||
@@ -38,3 +38,3 @@ | ||
$ node --interactive | ||
> const duo_web = require('duo_web'); | ||
> const duo_web = require('@duosecurity/duo_web'); | ||
> duo_web.sign_request(ikey, skey, akey, username); | ||
@@ -41,0 +41,0 @@ 'TX|...TX_SIGNATURE...==|...TX_HASH...:APP|...APP_SIGNATURE...==|...APP_HASH...' |
119
tests/duo.js
@@ -0,2 +1,4 @@ | ||
/* global describe it */ | ||
var Duo = require('../index') | ||
var assert = require('assert') | ||
@@ -14,34 +16,39 @@ var IKEY = 'DIXXXXXXXXXXXXXXXXXX' | ||
module.exports['Signing Checks'] = { | ||
'sign request with ikey/skey/akey and user': function (test) { | ||
describe('Signing Checks', function () { | ||
it('sign request with ikey/skey/akey and user', function (done) { | ||
var request_sig = Duo.sign_request(IKEY, SKEY, AKEY, USER) | ||
test.notEqual(request_sig, null, 'Invalid Request') | ||
test.done() | ||
}, | ||
'sign request without a user': function (test) { | ||
assert.notEqual(request_sig, null, 'Invalid Request') | ||
done() | ||
}) | ||
it('sign request without a user', function (done) { | ||
var request_sig = Duo.sign_request(IKEY, SKEY, AKEY, '') | ||
test.equal(request_sig, Duo.ERR_USER, 'Sign request user check failed') | ||
test.done() | ||
}, | ||
'sign request with invalid user': function (test) { | ||
assert.equal(request_sig, Duo.ERR_USER, 'Sign request user check failed') | ||
done() | ||
}) | ||
it('sign request with invalid user', function (done) { | ||
var request_sig = Duo.sign_request(IKEY, SKEY, AKEY, 'in|valid') | ||
test.equal(request_sig, Duo.ERR_USER, 'Sign request user check failed') | ||
test.done() | ||
}, | ||
'sign request with an invalid ikey': function (test) { | ||
assert.equal(request_sig, Duo.ERR_USER, 'Sign request user check failed') | ||
done() | ||
}) | ||
it('sign request with an invalid ikey', function (done) { | ||
var request_sig = Duo.sign_request('invalid', SKEY, AKEY, USER) | ||
test.equal(request_sig, Duo.ERR_IKEY, 'Sign request ikey check failed') | ||
test.done() | ||
}, | ||
'sign request with an invalid skey': function (test) { | ||
assert.equal(request_sig, Duo.ERR_IKEY, 'Sign request ikey check failed') | ||
done() | ||
}) | ||
it('sign request with an invalid skey', function (done) { | ||
var request_sig = Duo.sign_request(IKEY, 'invalid', AKEY, USER) | ||
test.equal(request_sig, Duo.ERR_SKEY, 'Sign request skey check failed') | ||
test.done() | ||
}, | ||
'sign request with an invalid akey': function (test) { | ||
assert.equal(request_sig, Duo.ERR_SKEY, 'Sign request skey check failed') | ||
done() | ||
}) | ||
it('sign request with an invalid akey', function (done) { | ||
var request_sig = Duo.sign_request(IKEY, SKEY, 'invalid', USER) | ||
test.equal(request_sig, Duo.ERR_AKEY, 'Sign request akey check failed') | ||
test.done() | ||
} | ||
} | ||
assert.equal(request_sig, Duo.ERR_AKEY, 'Sign request akey check failed') | ||
done() | ||
}) | ||
}) | ||
@@ -56,38 +63,38 @@ var request_sig = Duo.sign_request(IKEY, SKEY, AKEY, USER) | ||
module.exports['Verify Checks'] = { | ||
'verify request': function (test) { | ||
describe('Verify Checks', function () { | ||
it('verify request', function (done) { | ||
var user = Duo.verify_response(IKEY, SKEY, AKEY, INVALID_RESPONSE + ':' + valid_app_sig) | ||
test.equal(user, null, 'Invalid response check failed') | ||
test.done() | ||
}, | ||
'expire check': function (test) { | ||
assert.equal(user, null, 'Invalid response check failed') | ||
done() | ||
}) | ||
it('expire check', function (done) { | ||
var user = Duo.verify_response(IKEY, SKEY, AKEY, EXPIRED_RESPONSE + ':' + valid_app_sig) | ||
test.equal(user, null, 'Expired response check failed') | ||
test.done() | ||
}, | ||
'invalid app sig': function (test) { | ||
assert.equal(user, null, 'Expired response check failed') | ||
done() | ||
}) | ||
it('invalid app sig', function (done) { | ||
var user = Duo.verify_response(IKEY, SKEY, AKEY, FUTURE_RESPONSE + ':' + invalid_app_sig) | ||
test.equal(user, null, 'Invalid app sig check failed') | ||
test.done() | ||
}, | ||
'verify response on valid signature': function (test) { | ||
assert.equal(user, null, 'Invalid app sig check failed') | ||
done() | ||
}) | ||
it('verify response on valid signature', function (done) { | ||
var user = Duo.verify_response(IKEY, SKEY, AKEY, FUTURE_RESPONSE + ':' + valid_app_sig) | ||
test.equal(user, USER, 'verify response failed on valid signature') | ||
test.done() | ||
}, | ||
'invalid response format': function (test) { | ||
assert.equal(user, USER, 'verify response failed on valid signature') | ||
done() | ||
}) | ||
it('invalid response format', function (done) { | ||
var user = Duo.verify_response(IKEY, SKEY, AKEY, WRONG_PARAMS_RESPONSE + ':' + valid_app_sig) | ||
test.equal(user, null, 'Invalid response format check failed') | ||
test.done() | ||
}, | ||
'invalid app sig format': function (test) { | ||
assert.equal(user, null, 'Invalid response format check failed') | ||
done() | ||
}) | ||
it('invalid app sig format', function (done) { | ||
var user = Duo.verify_response(IKEY, SKEY, AKEY, FUTURE_RESPONSE + ':' + WRONG_PARAMS_APP) | ||
test.equal(user, null, 'Invalid app sig format check failed') | ||
test.done() | ||
}, | ||
'wrong ikey': function (test) { | ||
assert.equal(user, null, 'Invalid app sig format check failed') | ||
done() | ||
}) | ||
it('wrong ikey', function (done) { | ||
var user = Duo.verify_response(WRONG_IKEY, SKEY, AKEY, FUTURE_RESPONSE + ':' + valid_app_sig) | ||
test.equal(user, null, 'Wrong IKEY check failed') | ||
test.done() | ||
} | ||
} | ||
assert.equal(user, null, 'Wrong IKEY check failed') | ||
done() | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
41493
878
8
2