CVE-2025-46572
Description
passport-wsfed-saml2 provides passport strategy for both WS-fed and SAML2 protocol. A vulnerability present starting in version 3.0.5 up to and including version 4.6.3 allows an attacker to impersonate any user during SAML authentication by crafting a SAMLResponse. This can be done by using a valid SAML object that was signed by the configured IdP. Users are affected specifically when the service provider is using passport-wsfed-saml2 and a valid SAML document signed by the Identity Provider can be obtained. Version 4.6.4 contains a fix for the vulnerability.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
passport-wsfed-saml2npm | >= 3.0.5, < 4.6.4 | 4.6.4 |
Patches
1e5cf3cc2a537Upgrades (#190)
26 files changed · +3353 −2885
.eslintignore+2 −0 added@@ -0,0 +1,2 @@ +test +examples \ No newline at end of file
.eslintrc.json+53 −0 added@@ -0,0 +1,53 @@ +{ + "env": { + "node": true, + "es6": true, + "mocha": true + }, + "parserOptions": { + "ecmaVersion": 2022 + }, + "rules": { + "no-var": 2, + "no-bitwise": 2, + "curly": ["error", "all"], + "eqeqeq": 2, + "wrap-iife": [ + 2, + "any" + ], + "indent": [ + 2, + 2, + { + "SwitchCase": 1 + } + ], + "no-use-before-define": 2, + "new-cap": 2, + "no-caller": 2, + "quotes": [ + 2, + "single", + "avoid-escape" + ], + "no-undef": 2, + "strict": 0, + "no-unused-expressions": 2, + "no-eval": 2, + "dot-notation": 0, + "no-unused-vars": 2, + "comma-style": [ + 2, + "last" + ], + "no-useless-escape": "off", + "security/detect-non-literal-fs-filename": "off", + "security/detect-object-injection": "off", + "security/detect-unsafe-regex": "off", + "camelcase": 1 + }, + "extends": [ + "eslint:recommended" + ] +} \ No newline at end of file
.github/workflows/ci.yml+1 −1 modified@@ -8,7 +8,7 @@ jobs: strategy: matrix: - node-version: [12.x, 14.x, 16.x] + node-version: [18.x, 20.x, 22.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps:
.jshintrc+0 −39 removed@@ -1,39 +0,0 @@ -{ - "camelcase": false, - "curly": false, - - "node": true, - "esnext": true, - "bitwise": true, - "eqeqeq": true, - "immed": true, - "indent": 2, - "latedef": false, - "newcap": true, - "noarg": true, - "regexp": true, - "undef": true, - "strict": false, - "smarttabs": true, - "expr": true, - - "evil": true, - "browser": true, - "regexdash": true, - "wsh": true, - "trailing": true, - "sub": true, - "unused": true, - "laxcomma": true, - "nonbsp": true, - - "globals": { - "after": false, - "before": false, - "afterEach": false, - "beforeEach": false, - "describe": false, - "it": false, - "escape": false - } -}
lib/passport-wsfed-saml2/errors/SamlRequestParserError.js+13 −0 added@@ -0,0 +1,13 @@ +function SamlRequestParserError (message, detail, status) { + var err = Error.call(this, message); + err.name = 'SamlRequestParserError'; + err.message = message || 'Error parsing SAMLRequest'; + err.detail = detail; + err.status = status || 400; + return err; +} + +SamlRequestParserError.prototype = Object.create(Error.prototype); +SamlRequestParserError.prototype.constructor = SamlRequestParserError; + +module.exports = SamlRequestParserError; \ No newline at end of file
lib/passport-wsfed-saml2/saml.js+357 −323 modified@@ -1,429 +1,463 @@ -// credits to: https://github.com/bergie/passport-saml - -var crypto = require('crypto'); -var xpath = require('xpath'); -var xmlCrypto = require('xml-crypto'); -var EventEmitter = require('events'); +const crypto = require('crypto'); +const xpath = require('xpath'); +const xmlCrypto = require('xml-crypto'); +const EventEmitter = require('events'); const forge = require('node-forge'); const utils = require('./utils'); +const xmldom = require('@xmldom/xmldom'); -var ELEMENT_NODE = 1; +const ELEMENT_NODE = 1; -var SAML = function (options) { - this.options = options || {}; +const domParser = new xmldom.DOMParser(); - if (this.options.thumbprint) { - this.options.thumbprints = (this.options.thumbprints || []).concat([this.options.thumbprint]); +const getAuthContext20 = (samlAssertion) => { + const authnContext = xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='AuthnStatement']/*[local-name(.)='AuthnContext']/*[local-name(.)='AuthnContextClassRef']", samlAssertion); + if (authnContext.length === 0) { + return; } + return authnContext[0].textContent; +}; - if (!this.options.cert && (!this.options.thumbprints || this.options.thumbprints.length === 0)) { - throw new Error('You should set either a base64 encoded certificate or the thumbprints of the signing certificates'); +const getAttributeValues = (attribute) => { + if (!attribute || attribute.childNodes.length === 0) { + return; + } + const attributeValues = []; + for (let i = 0; i < attribute.childNodes.length; i++) { + if (attribute.childNodes[i].nodeType !== ELEMENT_NODE) { + continue; + } + attributeValues.push(attribute.childNodes[i].textContent); } - this.options.checkExpiration = (typeof this.options.checkExpiration !== 'undefined') ? this.options.checkExpiration : true; - // Note! It would be best to set this to true. But it's defaulting to false so as not to break login for expired certs. - this.options.checkCertExpiration = (typeof this.options.checkCertExpiration !== 'undefined') ? this.options.checkCertExpiration : false; - //clockskew in minutes - this.options.clockSkew = (typeof this.options.clockSkew === 'number' && this.options.clockSkew >= 0) ? this.options.clockSkew : 3; - this.options.checkAudience = (typeof this.options.checkAudience !== 'undefined') ? this.options.checkAudience : true; - this.options.checkRecipient = (typeof this.options.checkRecipient !== 'undefined') ? this.options.checkRecipient : true; - this.options.checkNameQualifier = (typeof this.options.checkNameQualifier !== 'undefined') ? this.options.checkNameQualifier : true; - this.options.checkSPNameQualifier = (typeof this.options.checkSPNameQualifier !== 'undefined') ? this.options.checkSPNameQualifier : true; - this.eventEmitter = this.options.eventEmitter || new EventEmitter(); + if (attributeValues.length === 1) { + return attributeValues[0]; + } + + return attributeValues; }; -SAML.prototype.validateSignature = function (xml, options, callback) { - var self = this; - xml = utils.parseSamlResponse(xml); +const getSessionIndex = (samlAssertion) => { + const authnStatement = xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='AuthnStatement']", samlAssertion); + const sessionIndex = authnStatement.length > 0 && authnStatement[0].attributes.length > 0 ? + authnStatement[0].getAttribute('SessionIndex') : undefined; + return sessionIndex || undefined; +}; - var signaturePath = this.options.signaturePath || options.signaturePath; - var signatures = xpath.select(signaturePath, xml); - if (signatures.length === 0) { - return callback(new Error('Signature is missing (xpath: ' + signaturePath + ')')); - } else if (signatures.length > 1) { - return callback(new Error('Signature was found more than one time (xpath: ' + signaturePath + ')')); - } - var signature = signatures[0]; - - var sig = new xmlCrypto.SignedXml(null, { idAttribute: 'AssertionID' }); - sig.keyInfoProvider = { - getKeyInfo: function (key) { - return "<X509Data></X509Data>"; - }, - getKey: function (keyInfo) { - - //If there's no embedded signing cert, use the configured cert through options - if(!keyInfo || keyInfo.length===0){ - if(!options.cert) throw new Error('options.cert must be specified for SAMLResponses with no embedded signing certificate'); - return utils.certToPEM(options.cert); - } +const getAttributes = (samlAssertion) => { + return xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='AttributeStatement']/*[local-name(.)='Attribute']", samlAssertion); +}; - //If there's an embedded signature and thumprints are provided check that - if (options.thumbprints && options.thumbprints.length > 0) { - var embeddedSignature = keyInfo[0].getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "X509Certificate"); - if (embeddedSignature.length > 0) { - var base64cer = embeddedSignature[0].firstChild.toString(); - var shasum = crypto.createHash('sha1'); - var der = new Buffer(base64cer, 'base64').toString('binary'); - shasum.update(der, 'latin1'); - self.calculatedThumbprint = shasum.digest('hex'); - - // using embedded cert, so options.cert is not used anymore - delete options.cert; - return utils.certToPEM(base64cer); - } - } +const getNameID11 = (samlAssertion) => { + let nameId = xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='AuthenticationStatement']/*[local-name(.)='Subject']/*[local-name(.)='NameIdentifier']", samlAssertion); - // If there's an embedded signature, but no thumprints are supplied, use options.cert - // either options.cert or options.thumbprints must be specified so at this point there - // must be an options.cert - return utils.certToPEM(options.cert); + if (nameId.length === 0) { + // only for backward compatibility with adfs + nameId = xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='AttributeStatement']/*[local-name(.)='Subject']/*[local-name(.)='NameIdentifier']", samlAssertion); + if (nameId.length === 0) { + return; } - }; + } - var valid; + return nameId[0].textContent; +}; - try { - sig.loadSignature(signature); - valid = sig.checkSignature(xml.toString()); +const getNameID20 = (samlAssertion) => { + const nameId = xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='Subject']/*[local-name(.)='NameID']", samlAssertion); + if (nameId.length === 0) { + return; + } + const element = nameId[0]; + const result = { + value: element.textContent, + }; - if (!self.extractAndValidateCertExpiration(xml, options.cert) && self.options.checkCertExpiration) { - return callback(new Error('The signing certificate is not currently valid.'), null); - } - } catch (e) { - if (e.message === 'PEM_read_bio_PUBKEY failed') { - return callback(new Error('The signing certificate is invalid (' + e.message + ')')); + [ + 'NameQualifier', + 'SPNameQualifier', + 'Format', + 'SPProvidedID' + ].forEach(function(key) { + const value = element.getAttribute(key); + if (!value) { + return; } - if (e.opensslErrorStack !== undefined) { - const err = new Error(`The signing certificate is invalid (${e.opensslErrorStack.join(', ')})`); - err.originalError = e; + result[key] = element.getAttribute(key); + }); - return callback(err); - } + return result; +}; - return callback(e); - } +function getKeyFn(options) { + return function (keyInfo) { + //If there's no embedded signing cert, use the configured cert through options + if (!keyInfo || keyInfo.length === 0 ){ + if (!options.cert) { + throw new Error('options.cert must be specified for SAMLResponses with no embedded signing certificate'); + } + return utils.certToPEM(options.cert); + } - if (!valid) { - return callback(new Error('Signature check errors: ' + sig.validationErrors)); - } + //If there's an embedded signature and thumbprints are provided check that + if (options.thumbprints && options.thumbprints.length > 0) { + const embeddedSignature = keyInfo.getElementsByTagNameNS('http://www.w3.org/2000/09/xmldsig#', 'X509Certificate'); + if (embeddedSignature.length > 0) { + const base64cer = embeddedSignature[0].firstChild.toString(); + const shasum = crypto.createHash('sha1'); + const der = new Buffer(base64cer, 'base64').toString('binary'); + shasum.update(der, 'latin1'); + const calculatedThumbprint = shasum.digest('hex').toUpperCase(); + const validThumbprint = options.thumbprints.some(function (thumbprint) { + return calculatedThumbprint === thumbprint.toUpperCase(); + }); + + if (!validThumbprint) { + throw new Error('Invalid thumbprint (configured: ' + options.thumbprints.join(', ').toUpperCase() + '. calculated: ' + calculatedThumbprint + ')' ); + } + // using embedded cert, so options.cert is not used anymore + delete options.cert; + return utils.certToPEM(base64cer); + } + } - if (options.cert) { - return callback(); + // If there's an embedded signature, but no thumbprints are supplied, use options.cert + // either options.cert or options.thumbprints must be specified so at this point there + // must be an options.cert + return utils.certToPEM(options.cert); } +} - if (options.thumbprints) { +class SAML { + constructor(options) { + this.options = options || {}; - var valid_thumbprint = options.thumbprints.some(function (thumbprint) { - return self.calculatedThumbprint.toUpperCase() === thumbprint.toUpperCase(); - }); + if (this.options.thumbprint) { + this.options.thumbprints = (this.options.thumbprints || []).concat([this.options.thumbprint]); + } - if (!valid_thumbprint) { - return callback(new Error('Invalid thumbprint (configured: ' + options.thumbprints.join(', ').toUpperCase() + '. calculated: ' + this.calculatedThumbprint.toUpperCase() + ')' )); + if (!this.options.cert && (!this.options.thumbprints || this.options.thumbprints.length === 0)) { + throw new Error('You should set either a base64 encoded certificate or the thumbprints of the signing certificates'); } - return callback(); + this.options.checkExpiration = (typeof this.options.checkExpiration !== 'undefined') ? this.options.checkExpiration : true; + // Note! It would be best to set this to true. But it's defaulting to false so as not to break login for expired certs. + this.options.checkCertExpiration = (typeof this.options.checkCertExpiration !== 'undefined') ? this.options.checkCertExpiration : false; + // clockskew in minutes + this.options.clockSkew = (typeof this.options.clockSkew === 'number' && this.options.clockSkew >= 0) ? this.options.clockSkew : 3; + this.options.checkAudience = (typeof this.options.checkAudience !== 'undefined') ? this.options.checkAudience : true; + this.options.checkRecipient = (typeof this.options.checkRecipient !== 'undefined') ? this.options.checkRecipient : true; + this.options.checkNameQualifier = (typeof this.options.checkNameQualifier !== 'undefined') ? this.options.checkNameQualifier : true; + this.options.checkSPNameQualifier = (typeof this.options.checkSPNameQualifier !== 'undefined') ? this.options.checkSPNameQualifier : true; + this.eventEmitter = this.options.eventEmitter || new EventEmitter(); + this.getUseTextContentDigestValue = options.getUseTextContentDigestValue; + + this.parser = domParser; } -}; -SAML.prototype.extractAndValidateCertExpiration = function (validatedSamlAssertion, optionsCert) { - // This accepts a validated SAML assertion and checks current time against the valid cert dates - const certNodes = validatedSamlAssertion.getElementsByTagName("X509Certificate"); + buildSignatureValidator (options) { + const sig = new xmlCrypto.SignedXml({ idAttribute: 'AssertionID', getCertFromKeyInfo: getKeyFn(options) }); + return sig; + } - const cert = certNodes.length > 0 ? certNodes[0].textContent : optionsCert; + validateSignature (str, options, callback) { + const xml = utils.parseSamlResponse(str, this.parser); - if (!cert) { return false; } + const signaturePath = this.options.signaturePath || options.signaturePath; + const signatures = xpath.select(signaturePath, xml); + if (signatures.length === 0) { + return callback(new Error('Signature is missing (xpath: ' + signaturePath + ')')); + } else if (signatures.length > 1) { + return callback(new Error('Signature was found more than one time (xpath: ' + signaturePath + ')')); + } + const signature = signatures[0]; + + let valid; + const opts = Object.assign({}, options, { cert: this.options.cert, thumbprints: this.options.thumbprints }); + const sig = this.buildSignatureValidator(opts); + try { + sig.loadSignature(signature); + valid = sig.checkSignature(utils.crlf2lf(str)); + + // TODO: this shouldn't be done until we have determined its completely valid, it should happen in `parseAssertion` + if (!this.extractAndValidateCertExpiration(xml, this.options.cert) && this.options.checkCertExpiration) { + return callback(new Error('The signing certificate is not currently valid.'), null); + } + } catch (e) { + if (e.message === 'PEM_read_bio_PUBKEY failed') { + return callback(new Error('The signing certificate is invalid (' + e.message + ')')); + } + if (e.opensslErrorStack !== undefined) { + const err = new Error(`The signing certificate is invalid (${e.opensslErrorStack.join(', ')})`); + err.originalError = e; - const parsedCert = forge.pki.certificateFromPem(utils.certToPEM(cert)); + return callback(err); + } - const nowDate = new Date(); + return callback(e); + } - // true if current date is before expiry AND after cert start date - if ( ! (nowDate > parsedCert.validity.notBefore && nowDate < parsedCert.validity.notAfter)) { - this.eventEmitter.emit('certificateExpirationValidationFailed', {}); - return false; - } + if (!valid) { + return callback(new Error('Signature check errors: ' + sig.references[0].validationError.message)); + } - return true; -}; + if (!sig.getSignedReferences().length) { + return callback(new Error('Could not validate Signature(s)')); + } -SAML.prototype.validateExpiration = function (samlAssertion, version) { - var self = this; - var conditions = xpath.select(".//*[local-name(.)='Conditions']", samlAssertion); - if (!conditions || conditions.length === 0) return true; + return callback(null, sig.getSignedReferences()[0]); + } - var notBefore = new Date(conditions[0].getAttribute('NotBefore')); - notBefore = notBefore.setMinutes(notBefore.getMinutes() - self.options.clockSkew); + extractAndValidateCertExpiration (validatedSamlAssertion, optionsCert) { + // This accepts a validated SAML assertion and checks current time against the valid cert dates + const certNodes = validatedSamlAssertion.getElementsByTagNameNS('http://www.w3.org/2000/09/xmldsig#', 'X509Certificate'); - var notOnOrAfter = new Date(conditions[0].getAttribute('NotOnOrAfter')); - notOnOrAfter = notOnOrAfter.setMinutes(notOnOrAfter.getMinutes() + self.options.clockSkew); - var now = new Date(); + const cert = certNodes.length > 0 ? certNodes[0].textContent : optionsCert; - if (now < notBefore || now > notOnOrAfter) - return false; + if (!cert) { return false; } - return true; -}; + const parsedCert = forge.pki.certificateFromPem(utils.certToPEM(cert)); -SAML.prototype.validateAudience = function (samlAssertion, realm, version) { - var audience; - if (version === '2.0') { - audience = xpath.select(".//*[local-name(.)='Conditions']/*[local-name(.)='AudienceRestriction']/*[local-name(.)='Audience']", samlAssertion); - } else { - audience = xpath.select(".//*[local-name(.)='Conditions']/*[local-name(.)='AudienceRestrictionCondition']/*[local-name(.)='Audience']", samlAssertion); - } + const nowDate = new Date(); - if (!audience || audience.length === 0) return false; - return utils.stringCompare(audience[0].textContent, realm); -}; + // true if current date is before expiry AND after cert start date + if ( ! (nowDate > parsedCert.validity.notBefore && nowDate < parsedCert.validity.notAfter)) { + this.eventEmitter.emit('certificateExpirationValidationFailed', {}); + return false; + } -SAML.prototype.validateNameQualifier = function (samlAssertion, issuer) { - var nameID = getNameID20(samlAssertion); - // NameQualifier is optional. Only validate if exists - if (!nameID || !nameID.Format || !nameID.NameQualifier) return true; + return true; + } - if ([ - 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', - 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' - ].indexOf(nameID.Format) == -1){ - // Ignore validation if the format is not persistent or transient + validateExpiration (samlAssertion) { + const conditions = xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='Conditions']", samlAssertion); + if (!conditions || conditions.length === 0) { return true; - } + } - return nameID.NameQualifier === issuer -}; + const condition = conditions[0]; + const notBefore = condition.getAttribute('NotBefore'); + const notOnOrAfter = condition.getAttribute('NotOnOrAfter'); -SAML.prototype.validateSPNameQualifier = function (samlAssertion, audience) { - var nameID = getNameID20(samlAssertion); - // SPNameQualifier is optional. Only validate if exists - if (!nameID || !nameID.Format || !nameID.SPNameQualifier) return true; + // no expiration defined. + if (!notBefore && !notOnOrAfter) { + return true + } - if ([ - 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', - 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' - ].indexOf(nameID.Format) == -1){ - // Ignore validation if the format is not persistent or transient - return true; - } + const now = new Date(); - return nameID.SPNameQualifier === audience -}; + if (notBefore) { + const notBeforeDate = new Date(notBefore); + notBeforeDate.setMinutes(notBeforeDate.getMinutes() - this.options.clockSkew); + if (now < notBefore) { + return false; + } + } + + if (notOnOrAfter) { + const notOnOrAfterDate = new Date(notOnOrAfter); + notOnOrAfterDate.setMinutes(notOnOrAfterDate.getMinutes() + this.options.clockSkew); + if (now > notOnOrAfterDate) { + return false; + } + } -// https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf -// Page 19 of 91 -// Recipient [Optional] -// A URI specifying the entity or location to which an attesting entity can present the assertion. For -// example, this attribute might indicate that the assertion must be delivered to a particular network -// endpoint in order to prevent an intermediary from redirecting it someplace else. -SAML.prototype.validateRecipient = function(samlAssertion, recipientUrl){ - var subjectConfirmationData = xpath.select(".//*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']", samlAssertion); - - // subjectConfirmationData is optional in the spec. Only validate if the assertion contains a recipient - if (!subjectConfirmationData || subjectConfirmationData.length === 0){ return true; } - var recipient = subjectConfirmationData[0].getAttribute('Recipient'); - - var valid = !recipient || recipient === recipientUrl; + validateAudience (samlAssertion, realm, version) { + let audience; + if (version === '2.0') { + audience = xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='Conditions']/*[local-name(.)='AudienceRestriction']/*[local-name(.)='Audience']", samlAssertion); + } else { + audience = xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='Conditions']/*[local-name(.)='AudienceRestrictionCondition']/*[local-name(.)='Audience']", samlAssertion); + } - if (!valid){ - this.eventEmitter.emit('recipientValidationFailed', { - configuredRecipient: recipientUrl, - assertionRecipient: recipient - }); + if (!audience || audience.length === 0) {return false;} + return utils.stringCompare(audience[0].textContent, realm); } - return valid; -}; + validateNameQualifier (samlAssertion, issuer) { + const nameID = getNameID20(samlAssertion); + // NameQualifier is optional. Only validate if exists + if (!nameID || !nameID.Format || !nameID.NameQualifier) { + return true; + } -SAML.prototype.parseAttributes = function (samlAssertion, version) { - function getAttributes(samlAssertion) { - var attributes = xpath.select(".//*[local-name(.)='AttributeStatement']/*[local-name(.)='Attribute']", samlAssertion); - return attributes; - } + if ([ + 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', + 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + ].indexOf(nameID.Format) === -1){ + // Ignore validation if the format is not persistent or transient + return true; + } - function getSessionIndex(samlAssertion) { - var authnStatement = xpath.select(".//*[local-name(.)='AuthnStatement']", samlAssertion); - var sessionIndex = authnStatement.length > 0 && authnStatement[0].attributes.length > 0 ? - authnStatement[0].getAttribute('SessionIndex') : undefined; - return sessionIndex || undefined; + return nameID.NameQualifier === issuer; } - function getNameID11(samlAssertion) { - var nameId = xpath.select(".//*[local-name(.)='AuthenticationStatement']/*[local-name(.)='Subject']/*[local-name(.)='NameIdentifier']", samlAssertion); + validateSPNameQualifier (samlAssertion, audience) { + const nameID = getNameID20(samlAssertion); + // SPNameQualifier is optional. Only validate if exists + if (!nameID || !nameID.Format || !nameID.SPNameQualifier) {return true;} - if (nameId.length === 0) { - // only for backward compatibility with adfs - nameId = xpath.select(".//*[local-name(.)='AttributeStatement']/*[local-name(.)='Subject']/*[local-name(.)='NameIdentifier']", samlAssertion); - if (nameId.length === 0) return; + if ([ + 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', + 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + ].indexOf(nameID.Format) === -1){ + // Ignore validation if the format is not persistent or transient + return true; } - return nameId[0].textContent; + return nameID.SPNameQualifier === audience; } - function getAttributeValues(attribute) { - if (!attribute || attribute.childNodes.length === 0) return; - var attributeValues = []; - for (var i = 0; i<attribute.childNodes.length; i++) { - if (attribute.childNodes[i].nodeType !== ELEMENT_NODE) continue; - attributeValues.push(attribute.childNodes[i].textContent); + // https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf + // Page 19 of 91 + // Recipient [Optional] + // A URI specifying the entity or location to which an attesting entity can present the assertion. For + // example, this attribute might indicate that the assertion must be delivered to a particular network + // endpoint in order to prevent an intermediary from redirecting it someplace else. + validateRecipient (samlAssertion, recipientUrl){ + const subjectConfirmationData = xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']", samlAssertion); + + // subjectConfirmationData is optional in the spec. Only validate if the assertion contains a recipient + if (!subjectConfirmationData || subjectConfirmationData.length === 0){ + return true; } - if (attributeValues.length === 1) return attributeValues[0]; + const recipient = subjectConfirmationData[0].getAttribute('Recipient'); - return attributeValues; - } + const valid = !recipient || recipient === recipientUrl; + + if (!valid){ + this.eventEmitter.emit('recipientValidationFailed', { + configuredRecipient: recipientUrl, + assertionRecipient: recipient + }); + } - function getAuthContext20(samlAssertion) { - var authnContext = xpath.select(".//*[local-name(.)='AuthnStatement']/*[local-name(.)='AuthnContext']/*[local-name(.)='AuthnContextClassRef']", samlAssertion); - if (authnContext.length === 0) return; - return authnContext[0].textContent; + return valid; } - var profile = {}; - var nameId; - var authContext; - var attributes = getAttributes(samlAssertion); - profile.sessionIndex = getSessionIndex(samlAssertion); - if (version === '2.0') { - for (var index in attributes) { - var attribute = attributes[index]; - var value = getAttributeValues(attribute); - profile[attribute.getAttribute('Name')] = value; - } + parseAttributes (samlAssertion, version) { + const profile = {}; + const attributes = getAttributes(samlAssertion); + profile.sessionIndex = getSessionIndex(samlAssertion); + if (version === '2.0') { + for (let index in attributes) { + const attribute = attributes[index]; + profile[attribute.getAttribute('Name')] = getAttributeValues(attribute); + } - nameId = getNameID20(samlAssertion); + const nameId = getNameID20(samlAssertion); - if (nameId) { - profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'] = nameId.value; - if(Object.keys(nameId).length > 1) { - profile['nameIdAttributes'] = nameId; + if (nameId) { + profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'] = nameId.value; + if(Object.keys(nameId).length > 1) { + profile['nameIdAttributes'] = nameId; + } } - } - authContext = getAuthContext20(samlAssertion); - if (authContext) { - profile['http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod'] = authContext; - } + const authContext = getAuthContext20(samlAssertion); + if (authContext) { + profile['http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod'] = authContext; + } + } else { + if (attributes) { + for (let index in attributes) { + const attribute = attributes[index]; + profile[attribute.getAttribute('AttributeNamespace') + '/' + attribute.getAttribute('AttributeName')] = getAttributeValues(attribute); + } + } - } else { - if (attributes) { - for (var index2 in attributes) { - var attribute2 = attributes[index2]; - var value2 = getAttributeValues(attribute2); - profile[attribute2.getAttribute('AttributeNamespace') + '/' + attribute2.getAttribute('AttributeName')] = value2; + const nameId = getNameID11(samlAssertion); + if (nameId) { + profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'] = typeof nameId === 'string' ? nameId : nameId['#']; } } - nameId = getNameID11(samlAssertion); - if (nameId) { - profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'] = typeof nameId === 'string' ? nameId : nameId['#']; - } + return profile; } - return profile; -}; - -SAML.prototype.validateSamlAssertion = function (samlAssertion, callback) { - var self = this; - - samlAssertion = utils.parseSamlResponse(samlAssertion); - - self.validateSignature(samlAssertion.toString(), { - cert: self.options.cert, - thumbprints: self.options.thumbprints, - signaturePath: "/*[local-name(.)='Assertion']/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']" }, function(err) { - if (err) return callback(err); - - self.parseAssertion(samlAssertion, callback); - }); -}; + validateSamlAssertion (samlAssertionStr, options, callback) { + this.validateSignature(samlAssertionStr, { + meta: options.meta, + signaturePath: "//*[local-name(.)='Assertion'][1]/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']" }, (err, signed) => { + if (err) {return callback(err);} -SAML.prototype.parseAssertion = function(samlAssertion, callback) { - var self = this; - if (self.options.extractSAMLAssertion){ - samlAssertion = self.options.extractSAMLAssertion(samlAssertion); + this.parseAssertion(signed, callback); + }); } - samlAssertion = utils.parseSamlResponse(samlAssertion); + parseAssertion (samlAssertion, callback) { + if (this.options.extractSAMLAssertion){ + samlAssertion = this.options.extractSAMLAssertion(samlAssertion); + } - if (!samlAssertion.getAttribute) - samlAssertion = samlAssertion.documentElement; + samlAssertion = utils.parseSamlAssertion(samlAssertion, this.parser); - const version = utils.getSamlAssertionVersion(samlAssertion); - if (!version){ - // Note that this assumes any version returned by getSamlAssertionVersion is supported. - return callback(new Error('SAML Assertion version not supported, or not defined'), null); - } + if (!samlAssertion.getAttribute) { + samlAssertion = samlAssertion.documentElement; + } - if (self.options.checkExpiration && !self.validateExpiration(samlAssertion, version)) { - return callback(new Error('assertion has expired.'), null); - } + if (samlAssertion.localName !== 'Assertion') { + return callback(new Error('saml response does not contain an Assertion element')); + } - if (self.options.checkAudience && !self.validateAudience(samlAssertion, self.options.realm, version)) { - return callback(new Error('Audience is invalid. Configured: ' + self.options.realm), null); - } + const version = utils.getSamlAssertionVersion(samlAssertion); + if (!version){ + // Note that this assumes any version returned by getSamlAssertionVersion is supported. + return callback(new Error('SAML Assertion version not supported, or not defined'), null); + } - if (!self.validateRecipient(samlAssertion, self.options.recipientUrl)) { - if (self.options.checkRecipient){ - return callback(new Error('Recipient is invalid. Configured: ' + self.options.recipientUrl), null); + if (this.options.checkExpiration && !this.validateExpiration(samlAssertion, version)) { + return callback(new Error('assertion has expired.'), null); } - } - var profile = self.parseAttributes(samlAssertion, version); + if (this.options.checkAudience && !this.validateAudience(samlAssertion, this.options.realm, version)) { + return callback(new Error('Audience is invalid. Configured: ' + this.options.realm), null); + } - var issuer; - if (version === '2.0') { - var issuerNode = xpath.select(".//*[local-name(.)='Issuer']", samlAssertion); - if (issuerNode.length > 0) issuer = issuerNode[0].textContent; - } else { - issuer = samlAssertion.getAttribute('Issuer'); - } + if (!this.validateRecipient(samlAssertion, this.options.recipientUrl)) { + if (this.options.checkRecipient){ + return callback(new Error('Recipient is invalid. Configured: ' + this.options.recipientUrl), null); + } + } + const profile = this.parseAttributes(samlAssertion, version); - this.eventEmitter.emit('parseAssertion', { + let issuer; + if (version === '2.0') { + const issuerNode = xpath.select("//*[local-name(.)='Assertion'][1]/*[local-name(.)='Issuer']", samlAssertion); + if (issuerNode.length > 0) { + issuer = issuerNode[0].textContent; + } + } else { + issuer = samlAssertion.getAttribute('Issuer'); + } + + this.eventEmitter.emit('parseAssertion', { issuer: issuer, version: version, }); - profile.issuer = issuer; - - // Validate the name qualifier in the NameID element if found with the audience - if (self.options.checkNameQualifier && !self.validateNameQualifier(samlAssertion, issuer)){ - return callback(new Error('NameQualifier attribute in the NameID element does not match ' + issuer), null); - } - - // Validate the SP name qualifier in the NameID element if found with the issuer - if (self.options.checkSPNameQualifier && !self.validateSPNameQualifier(samlAssertion, self.options.realm)){ - return callback(new Error('SPNameQualifier attribute in the NameID element does not match ' + self.options.realm), null); - } - - if (!profile.email && profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']) { - profile.email = profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']; - } + profile.issuer = issuer; - callback(null, profile); -}; + // Validate the name qualifier in the NameID element if found with the audience + if (this.options.checkNameQualifier && !this.validateNameQualifier(samlAssertion, issuer)) { + return callback(new Error('NameQualifier attribute in the NameID element does not match ' + issuer), null); + } -function getNameID20(samlAssertion) { - var nameId = xpath.select(".//*[local-name(.)='Subject']/*[local-name(.)='NameID']", samlAssertion); - if (nameId.length === 0) return; - var element = nameId[0]; - var result = { - value: element.textContent, - }; + // Validate the SP name qualifier in the NameID element if found with the issuer + if (this.options.checkSPNameQualifier && !this.validateSPNameQualifier(samlAssertion, this.options.realm)){ + return callback(new Error('SPNameQualifier attribute in the NameID element does not match ' + this.options.realm), null); + } - ['NameQualifier', - 'SPNameQualifier', - 'Format', - 'SPProvidedID'].forEach(function(key) { - var value = element.getAttribute(key); - if (!value) return; - result[key] = element.getAttribute(key); - }); + if (!profile.email && profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']) { + profile.email = profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']; + } - return result; + return callback(null, profile); + } } -exports.SAML = SAML; +exports.SAML = SAML; \ No newline at end of file
lib/passport-wsfed-saml2/samlp.js+236 −213 modified@@ -1,101 +1,81 @@ -var xpath = require('xpath'); -var qs = require('querystring'); -var zlib = require('zlib'); -var xtend = require('xtend'); -var url = require('url'); -var xmlenc = require('xml-encryption'); -var crypto = require('crypto'); -var querystring = require('querystring'); -var SignedXml = require('xml-crypto').SignedXml; -var templates = require('./templates'); -var EventEmitter = require('events'); -var validUrl = require('valid-url'); - -var utils = require('./utils'); -var AuthenticationFailedError = require('./errors/AuthenticationFailedError'); - -var BINDINGS = { +const xpath = require('xpath'); +const qs = require('querystring'); +const zlib = require('zlib'); +const xtend = require('xtend'); +const url = require('url'); +const xmlenc = require('xml-encryption'); +const crypto = require('crypto'); +const querystring = require('querystring'); +const xmlCrypto = require('xml-crypto'); +const templates = require('./templates'); +const EventEmitter = require('events'); +const validUrl = require('valid-url'); +const xmldom = require('@xmldom/xmldom'); + +const domParser = new xmldom.DOMParser(); +const utils = require('./utils'); +const AuthenticationFailedError = require('./errors/AuthenticationFailedError'); + +const saml2Namespace = 'urn:oasis:names:tc:SAML:2.0:assertion'; + +const BINDINGS = { HTTP_POST: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', HTTP_REDIRECT: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' }; -var ErrorMessages = { +const ErrorMessages = { 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch': 'The SAML responder could not process the request because the version of the request message was incorrect.', 'urn:oasis:names:tc:SAML:2.0:status:Requester' : 'The request could not be performed due to an error on the part of the requester', 'urn:oasis:names:tc:SAML:2.0:status:Responder' : 'The request could not be performed due to an error on the part of the SAML responder or SAML authority', 'urn:oasis:names:tc:SAML:2.0:status:AuthnFailed' : 'The responding provider was unable to successfully authenticate the principal' }; -function ignoreValidationFunction(samlResponseID, done){ - return done(); -} - -var encodingMappings = { +const encodingMappings = { 'ISO-8859-1': 'binary', 'UTF-8': 'utf8' }; -var Samlp = module.exports = function Samlp (options, saml) { - this.options = options || {}; - - if (this.options.thumbprint) { - this.options.thumbprints = (this.options.thumbprints || []).concat([this.options.thumbprint]); - } - - if (typeof options.deflate === 'undefined') { - this.options.deflate = true; - } - - this.options.checkDestination = (typeof this.options.checkDestination !== 'undefined') ? this.options.checkDestination : true; - this.options.checkResponseID = (typeof this.options.checkResponseID !== 'undefined') ? this.options.checkResponseID : true; - this.options.checkInResponseTo = (typeof this.options.checkInResponseTo !== 'undefined') ? this.options.checkInResponseTo : true; - - this.eventEmitter = this.options.eventEmitter || new EventEmitter(); - this._saml = saml; - - this.isValidResponseID = this.options.isValidResponseID || ignoreValidationFunction; - this.isValidInResponseTo = this.options.isValidInResponseTo || ignoreValidationFunction; - - this.default_encoding = encodingMappings[this.options.default_encoding] || 'utf8'; -}; +function ignoreValidationFunction(samlResponseID, done){ + return done(); +} function getProp(obj, path) { return path.split('.').reduce(function (prev, curr) { return prev[curr]; }, obj); } -var supplant = function (tmpl, o) { +const supplant = function (tmpl, o) { return tmpl.replace(/\@\@([^\@]*)\@\@/g, - function (a, b) { - var r = getProp(o, b); - return typeof r === 'string' || typeof r === 'number' ? r : a; - } + function (a, b) { + const r = getProp(o, b); + return typeof r === 'string' || typeof r === 'number' ? r : a; + } ); }; -var trimXml = function (xml) { +const trimXml = function (xml) { return xml.replace(/\r\n/g, '') .replace(/\n/g,'') .replace(/>(\s*)</g, '><') //unindent .trim(); }; -var removeHeaders = function (cert) { - var pem = /-----BEGIN (\w*)-----([^-]*)-----END (\w*)-----/g.exec(cert.toString()); +const removeHeaders = function (cert) { + const pem = /-----BEGIN (\w*)-----([^-]*)-----END (\w*)-----/g.exec(cert.toString()); if (pem && pem.length > 0) { return pem[2].replace(/[\n|\r\n]/g, ''); } return null; }; -var sign = function (content, key, algorithm) { - var signer = crypto.createSign(algorithm.toUpperCase()); +const sign = function (content, key, algorithm) { + const signer = crypto.createSign(algorithm.toUpperCase()); signer.update(content, 'latin1'); return signer.sign(key, 'base64'); }; -var algorithms = { +const algorithms = { signature: { 'rsa-sha256': 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', 'rsa-sha1': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' @@ -113,10 +93,10 @@ function collectAncestorNamespaces(node, nameSpaces = [], maxDeep = 5){ const parent = node.parentNode; - if(parent.attributes && parent.attributes.length > 0){ + if (parent.attributes && parent.attributes.length > 0){ for(let i=0;i<parent.attributes.length;i++){ const attr = parent.attributes[i]; - if(attr && attr.nodeName && attr.nodeName.search(/^xmlns:/) !== -1){ + if (attr && attr.nodeName && attr.nodeName.search(/^xmlns:/) !== -1){ nameSpaces.push({key: attr.nodeName, value: attr.nodeValue}); } } @@ -125,24 +105,55 @@ function collectAncestorNamespaces(node, nameSpaces = [], maxDeep = 5){ return collectAncestorNamespaces(parent, nameSpaces, maxDeep - 1); } -Samlp.prototype = { - getSamlRequestParams: function (opts, callback) { - var options = xtend(opts || {}, this.options); +function generateInstant() { + const date = new Date(); + return date.getUTCFullYear() + '-' + ('0' + (date.getUTCMonth()+1)).slice(-2) + '-' + ('0' + date.getUTCDate()).slice(-2) + 'T' + ('0' + date.getUTCHours()).slice(-2) + ':' + ('0' + date.getUTCMinutes()).slice(-2) + ':' + ('0' + date.getUTCSeconds()).slice(-2) + 'Z'; +} - var idpUrl = options.identityProviderUrl; +function stripQueryAndFragmentFromURL(url) { + return url.split('#')[0].split('?')[0]; +} + +class Samlp { + constructor(options, saml) { + this.options = options || {}; + + if (typeof options.deflate === 'undefined') { + this.options.deflate = true; + } + + this.options.checkDestination = (typeof this.options.checkDestination !== 'undefined') ? this.options.checkDestination : true; + this.options.checkResponseID = (typeof this.options.checkResponseID !== 'undefined') ? this.options.checkResponseID : true; + this.options.checkInResponseTo = (typeof this.options.checkInResponseTo !== 'undefined') ? this.options.checkInResponseTo : true; + + this.eventEmitter = this.options.eventEmitter || new EventEmitter(); + this._saml = saml; + + this.isValidResponseID = this.options.isValidResponseID || ignoreValidationFunction; + this.isValidInResponseTo = this.options.isValidInResponseTo || ignoreValidationFunction; + + this.defaultEncoding = encodingMappings[this.options.default_encoding] || 'utf8'; + + this.parser = domParser; + } + + getSamlRequestParams (opts, callback) { + const options = xtend(opts || {}, this.options); + + const idpUrl = options.identityProviderUrl; if (typeof idpUrl !== 'string' || !validUrl.isWebUri(idpUrl)) { return callback(new Error(`Invalid identity provider URL: ${JSON.stringify(idpUrl)}`)); } - var signatureAlgorithm = options.signatureAlgorithm || 'rsa-sha256'; - var digestAlgorithm = options.digestAlgorithm || 'sha256'; + const signatureAlgorithm = options.signatureAlgorithm || 'rsa-sha256'; + const digestAlgorithm = options.digestAlgorithm || 'sha256'; - var assert_and_destination = templates.assert_and_destination({ + const assert_and_destination = templates.assert_and_destination({ Destination: idpUrl, AssertionConsumerServiceURL: options.callback }); - var model = { + let model = { ID: options.request_id, IssueInstant: generateInstant(), Issuer: options.realm, @@ -159,23 +170,23 @@ Samlp.prototype = { model = xtend(model, options.requestContext); } - var SAMLRequest; - var rawRequest; + let SAMLRequest; + let rawRequest; if (options.requestTemplate) { try { - rawRequest = supplant(options.requestTemplate, model) + rawRequest = supplant(options.requestTemplate, model); } catch (e) { - return callback(new Error('Malformed template passed. Could not parse.')) + return callback(new Error('Malformed template passed. Could not parse.')); } } else { rawRequest = templates.samlrequest(model); } SAMLRequest = trimXml(rawRequest); - var parsedUrl = url.parse(idpUrl, true); - var params = { + const parsedUrl = url.parse(idpUrl, true); + const params = { SAMLRequest: null, RelayState: options.RelayState || (parsedUrl.query && parsedUrl.query.RelayState) || '' }; @@ -184,20 +195,23 @@ Samlp.prototype = { // HTTP-POST or HTTP-Redirect without deflate encoding if (options.signingKey) { // xml with embedded Signature - var sig = new SignedXml(null, { signatureAlgorithm: algorithms.signature[signatureAlgorithm] }); - sig.addReference( - "//*[local-name(.)='AuthnRequest' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:protocol']", - ["http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#"], - algorithms.digest[digestAlgorithm]); - - sig.keyInfoProvider = { - getKeyInfo: function () { - return '<X509Data><X509Certificate>' + removeHeaders(options.signingKey.cert) + '</X509Certificate></X509Data>'; - } - }; - sig.signingKey = options.signingKey.key; + const sig = new xmlCrypto.SignedXml({ + privateKey: options.signingKey.key, + signatureAlgorithm: algorithms.signature[signatureAlgorithm], + canonicalizationAlgorithm: 'http://www.w3.org/2001/10/xml-exc-c14n#', + getKeyInfoContent: () => { + return `<X509Data><X509Certificate>${removeHeaders(options.signingKey.cert)}</X509Certificate></X509Data>`; + }, + }); + sig.addReference({ + xpath: "//*[local-name(.)='AuthnRequest' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:protocol']", + transforms: ['http://www.w3.org/2000/09/xmldsig#enveloped-signature', 'http://www.w3.org/2001/10/xml-exc-c14n#'], + digestAlgorithm: algorithms.digest[digestAlgorithm] + }); try { + // we are not converting SAMLRequest into a DOM before sending to xml-crypto because at the current time we allow the following test: + // invalid CDATA xml that does not cause an error. This probably *should* cause an error, but it doesn't. sig.computeSignature(SAMLRequest, { location: { reference: "//*[local-name(.)='Issuer']", action: 'after' } }); // Signature element must be located after Issuer } catch (e) { return callback(new Error('fail to compute signature')); @@ -212,7 +226,7 @@ Samlp.prototype = { // HTTP-Redirect with deflate encoding (http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf - section 3.4.4.1) zlib.deflateRaw(new Buffer(SAMLRequest), function (err, buffer) { - if (err) return callback(err); + if (err) {return callback(err);} params.SAMLRequest = buffer.toString('base64'); @@ -236,165 +250,173 @@ Samlp.prototype = { callback(null, params); }); - }, + } - getSamlRequestUrl: function (opts, callback) { - var options = xtend(opts || {}, this.options); + getSamlRequestUrl (opts, callback) { + const options = xtend(opts || {}, this.options); this.getSamlRequestParams(options, function (err, params) { - if (err) return callback(err); + if (err) {return callback(err);} - var parsedUrl = url.parse(options.identityProviderUrl, true); - var samlRequestUrl = stripQueryAndFragmentFromURL(options.identityProviderUrl) + '?' + qs.encode(xtend(parsedUrl.query, params)); + let parsedUrl = url.parse(options.identityProviderUrl, true); + let samlRequestUrl = stripQueryAndFragmentFromURL(options.identityProviderUrl) + '?' + qs.encode(xtend(parsedUrl.query, params)); if (parsedUrl.hash !== null) { samlRequestUrl += parsedUrl.hash; } return callback(null, samlRequestUrl); }); - }, + } - getSamlRequestForm: function (opts, callback) { - var options = xtend(opts || {}, this.options); + getSamlRequestForm (opts, callback) { + const options = xtend(opts || {}, this.options); this.getSamlRequestParams(options, function (err, params) { - if (err) return callback(err); + if (err) {return callback(err);} return callback(null, templates.form({ postUrl: options.identityProviderUrl, RelayState: params.RelayState, SAMLRequest: params.SAMLRequest })); }); - }, + } - decodeResponse: function(req) { - var decoded = new Buffer(req.body['SAMLResponse'], 'base64').toString(this.default_encoding); + decodeResponse (req) { + let decoded = new Buffer(req.body['SAMLResponse'], 'base64').toString(this.defaultEncoding); const encoding = utils.getEncoding(decoded); - if (encoding && encodingMappings[encoding] && encodingMappings[encoding] !== this.default_encoding){ + if (encoding && encodingMappings[encoding] && encodingMappings[encoding] !== this.defaultEncoding){ // Encoding defers from the one configured, decode again with the correct value decoded = new Buffer(req.body['SAMLResponse'], 'base64').toString(encodingMappings[encoding]); } return decoded; - }, - - extractAssertion: function(samlpResponse, callback) { - samlpResponse = utils.parseSamlResponse(samlpResponse); - const saml2Namespace = 'urn:oasis:names:tc:SAML:2.0:assertion'; - - function done(err, assertion) { - if (err) { - return callback(err); - } - - assertion = utils.parseSamlAssertion(assertion); - - // copy all ancestor namespaces see https://github.com/auth0/xml-crypto/blob/d36a1bc0af40a5a3eec9c0c7b6b3f87bb0a0bca1/lib/signed-xml.js#L390-L392 - // When we extract the assertion for later usage, this assertion wont include all name spaces. All namespaces from parents - // nodes are used to calculate the digest. - collectAncestorNamespaces(assertion) - .filter((attr) => !assertion.getAttribute(attr.key)) - .forEach((attr) => assertion.setAttribute(attr.key, attr.value)); - - callback(null, assertion); - } + } - var foundAssertions = xpath.select("//*[local-name(.)='Assertion']", samlpResponse); + // samlpResponse may be both a string or a DOM depending on the caller. + // if the assertion is encrypted returns: + // Document|DOM of the embedded encrypted assertion, + // boolean saying there was decryption + // str of the original XML of the encrypted assertion + // else: + // Node|DOM of the assertion included in the original XML + // boolean saying there was no decryption + extractAssertion (samlpResponse, callback) { + samlpResponse = utils.parseSamlResponse(samlpResponse, this.parser); + + const foundAssertions = xpath.select("//*[local-name(.)='Assertion']", samlpResponse); if (foundAssertions.length > 1) { - return done(new Error('A SAMLResponse can contain only one Assertion element.')); + return callback(new Error('A SAMLResponse can contain only one Assertion element.')); } // After being sure no more "Assertion" elements are found, we extract it from the expected place - var assertions = xpath.select("/*[local-name(.)='Response']/*[local-name(.)='Assertion' and namespace-uri(.)='" + saml2Namespace + "']", samlpResponse); - var token = assertions[0]; + const assertions = xpath.select("/*[local-name(.)='Response'][1]/*[local-name(.)='Assertion' and namespace-uri(.)='" + saml2Namespace + "']", samlpResponse); + const token = assertions[0]; if (!token) { // check for encrypted assertion - var encryptedAssertionPath = "/*[local-name(.)='Response']/*[local-name(.)='EncryptedAssertion' and namespace-uri(.)='" + saml2Namespace + "']"; - var encryptedAssertion = xpath.select(encryptedAssertionPath, samlpResponse); + const encryptedAssertionPath = "/*[local-name(.)='Response'][1]/*[local-name(.)='EncryptedAssertion' and namespace-uri(.)='" + saml2Namespace + "']"; + const encryptedAssertion = xpath.select(encryptedAssertionPath, samlpResponse); if (encryptedAssertion.length > 1) { - return done(new Error('A SAMLResponse can contain only one EncryptedAssertion element.')); + return callback(new Error('A SAMLResponse can contain only one EncryptedAssertion element.')); } - var encryptedToken = encryptedAssertion[0]; + const encryptedToken = encryptedAssertion[0]; if (encryptedToken) { - var encryptedData = encryptedToken.getElementsByTagNameNS('http://www.w3.org/2001/04/xmlenc#', 'EncryptedData')[0]; + const encryptedData = encryptedToken.getElementsByTagNameNS('http://www.w3.org/2001/04/xmlenc#', 'EncryptedData')[0]; if (!encryptedData) { - return done(new Error('EncryptedData not found.')); + return callback(new Error('EncryptedData not found.')); } if (!this.options.decryptionKey) { - return done(new Error('Assertion is encrypted. Please set options.decryptionKey with your decryption private key.')); + return callback(new Error('Assertion is encrypted. Please set options.decryptionKey with your decryption private key.')); } - return xmlenc.decrypt(encryptedData, { - key: this.options.decryptionKey, + return xmlenc.decrypt(encryptedData, { + key: this.options.decryptionKey, autopadding: this.options.autopadding, disallowDecryptionWithInsecureAlgorithm: false, warnInsecureAlgorithm: false - }, done); + }, (err, decryptedAssertion) => { + if (err) { + return callback(err) + } + const assertion = utils.parseSamlAssertion(decryptedAssertion, this.parser); + const foundAssertions = xpath.select("//*[local-name(.)='Assertion']", assertion); + if (foundAssertions.length > 1) { + return callback(new Error('A EncryptedAssertion can contain only one Assertion element.')); + } + // After being sure no more "Assertion" elements are found, we extract it from the expected place + const assertions = xpath.select("/*[local-name(.)='Assertion' and namespace-uri(.)='" + saml2Namespace + "']", assertion); + // if there are 0 matches, let the caller handle it + return callback(null, assertions[0], true, decryptedAssertion); + }); } } - done(null, token); - }, + callback(null, token, false); + } - getSamlStatus: function (samlResponse) { - var status = {}; + getSamlStatus (samlResponse) { + let status = {}; - samlResponse = utils.parseSamlResponse(samlResponse); + samlResponse = utils.parseSamlResponse(samlResponse, this.parser); // status code - var statusCodeXml = xpath.select("//*[local-name(.)='Status']/*[local-name(.)='StatusCode']", samlResponse)[0]; + const statusCodeXml = xpath.select("/*[local-name(.)='Response'][1]/*[local-name(.)='Status']/*[local-name(.)='StatusCode']", samlResponse)[0]; if (statusCodeXml) { status.code = statusCodeXml.getAttribute('Value'); // status sub code - var statusSubCodeXml = xpath.select("//*[local-name(.)='Status']/*[local-name(.)='StatusCode']/*[local-name(.)='StatusCode']", samlResponse)[0]; + const statusSubCodeXml = xpath.select("/*[local-name(.)='Response'][1]/*[local-name(.)='Status']/*[local-name(.)='StatusCode']/*[local-name(.)='StatusCode']", samlResponse)[0]; if (statusSubCodeXml) { status.subCode = statusSubCodeXml.getAttribute('Value'); } } // status message - var samlStatusMsgXml = xpath.select("//*[local-name(.)='Status']/*[local-name(.)='StatusMessage']", samlResponse)[0]; + const samlStatusMsgXml = xpath.select("/*[local-name(.)='Response'][1]/*[local-name(.)='Status']/*[local-name(.)='StatusMessage']", samlResponse)[0]; if (samlStatusMsgXml) { status.message = samlStatusMsgXml.textContent; } // status detail - var samlStatusDetailXml = xpath.select("//*[local-name(.)='Status']/*[local-name(.)='StatusDetail']", samlResponse)[0]; + const samlStatusDetailXml = xpath.select("/*[local-name(.)='Response'][1]/*[local-name(.)='Status']/*[local-name(.)='StatusDetail']", samlResponse)[0]; if (samlStatusDetailXml) { status.detail = samlStatusDetailXml.textContent; } return status; - }, - - validateSamlResponse: function (samlResponse, callback) { - var self = this; + } - samlResponse = utils.parseSamlResponse(samlResponse); + validateSamlResponse (samlResponseStr, meta, callback) { + if (typeof samlResponseStr !== 'string') { + throw new Error('samlResponse must be a string'); + } + const samlResponse = utils.parseSamlResponse(samlResponseStr, this.parser); - // Check that the saml Resopnse actually has a Response object - var responseXMLs = xpath.select("//*[local-name(.)='Response']", samlResponse); + // Check that the saml Response actually has a Response object + const responseXMLs = xpath.select("//*[local-name(.)='Response']", samlResponse); if (responseXMLs.length === 0) { return callback(new Error('XML is not a valid saml response')); } if (responseXMLs.length > 1) { return callback(new Error('SAMLResponse should be unique')); } - var responseXML = responseXMLs[0]; + const responseXML = responseXMLs[0]; - self.isValidResponseID(responseXML.getAttribute('ID'), function(err){ - if (err && self.options.checkResponseID) { return callback(err); } + this.isValidResponseID(responseXML.getAttribute('ID'), (err) => { + if (err && this.options.checkResponseID) { + return callback(err); + } - var inResponseTo = responseXML.getAttribute('InResponseTo'); + const inResponseTo = responseXML.getAttribute('InResponseTo'); - self.isValidInResponseTo(inResponseTo, function(err){ - if (err && self.options.checkInResponseTo) { return callback(err); } + this.isValidInResponseTo(inResponseTo, (err) => { + if (err && this.options.checkInResponseTo) { + return callback(err); + } - var destination = responseXML.getAttribute('Destination'); + const destination = responseXML.getAttribute('Destination'); // https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf // Page 36 of 91 @@ -404,42 +426,46 @@ Samlp.prototype = { // protocol bindings. If it is present, the actual recipient MUST check that the URI reference identifies the // location at which the message was received. If it does not, the request MUST be discarded. Some // protocol bindings may require the use of this attribute (see [SAMLBind]). - if (destination && destination !== self.options.destinationUrl){ - self.eventEmitter.emit('destinationValidationFailed', { - configuredDestination: self.options.destinationUrl, + if (destination && destination !== this.options.destinationUrl) { + this.eventEmitter.emit('destinationValidationFailed', { + configuredDestination: this.options.destinationUrl, assertionDestination: destination }); - if (self.options.checkDestination){ - return callback(new Error('Destination endpoint ' + destination + ' did not match ' + self.options.destinationUrl)); + if (this.options.checkDestination) { + return callback(new Error('Destination endpoint ' + destination + ' did not match ' + this.options.destinationUrl)); } } // check status - var samlStatus = self.getSamlStatus(samlResponse); + const samlStatus = this.getSamlStatus(responseXML); // Check if this is a known error - var errorMessage = ErrorMessages[samlStatus.subCode] || - ErrorMessages[samlStatus.code]; + const errorMessage = ErrorMessages[samlStatus.subCode] || + ErrorMessages[samlStatus.code]; if (errorMessage) { // Return auth failed with the actual message or a friendly message return callback (new AuthenticationFailedError(samlStatus.message || errorMessage, samlStatus.detail)); } // extract assertion - self.extractAssertion(samlResponse, function (err, assertion) { + this.extractAssertion(responseXML, (err, assertionDom, encrypted, assertionStr) => { if (err) { return callback(err); } - if (!assertion) { + if (!assertionDom) { return callback(new Error('saml response does not contain an Assertion element (Status: ' + samlStatus.code + ')')); } - var samlResponseSignaturePath = "/*[local-name(.)='Response']/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']"; - var isResponseSigned = xpath.select(samlResponseSignaturePath, samlResponse).length > 0; - var samlAssertionSignaturePath = ".//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']"; - var isAssertionSigned = xpath.select(samlAssertionSignaturePath, assertion).length > 0; + const samlResponseSignaturePath = "/*[local-name(.)='Response'][1]/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']"; + const isResponseSigned = xpath.select(samlResponseSignaturePath, responseXML).length > 0; - self.eventEmitter.emit('SAMLResponse:signatures', { + const samlAssertionSignaturePath = encrypted ? + "/*[local-name(.)='Assertion'][1]/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']" : + "/*[local-name(.)='Response'][1]/*[local-name(.)='Assertion'][1]/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']"; + + const isAssertionSigned = xpath.select(samlAssertionSignaturePath, assertionDom).length > 0; + + this.eventEmitter.emit('SAMLResponse:signatures', { isResponseSigned: isResponseSigned, isAssertionSigned: isAssertionSigned }); @@ -448,63 +474,60 @@ Samlp.prototype = { return callback(new Error('neither the response nor the assertion are signed')); } - if (isAssertionSigned) { - var assertionSignature = xpath.select(samlAssertionSignaturePath, assertion)[0]; - if (assertionSignature.prefix) { - try { - var dsigNamespace = assertionSignature.lookupNamespaceURI(assertionSignature.prefix); - if (dsigNamespace && !assertionSignature.getAttribute('xmlns:' + assertionSignature.prefix)) { - // saml assertion signature has a prefix but namespace is defined on parent, copy it to assertion - assertionSignature.setAttribute('xmlns:' + assertionSignature.prefix, dsigNamespace); - } - } catch(e) {} - } + if (isAssertionSigned) { + const assertionSignature = xpath.select(samlAssertionSignaturePath, assertionDom)[0]; - // If we find that a namespace was defined in resopnse and is used in assertion, we copy it to the assertion element + // If we find that a namespace was defined in response and is used in assertion, we copy it to the assertion element if (responseXML.attributes) { - var length = responseXML.attributes.length; - for (var i = 0; i < length; ++i) { - var attr = responseXML.attributes[i]; - // If attribute is a namespace, and is the signature prefix and is used in Assertion, cpy it to assertion - if (attr.name.indexOf("xmlns") === 0 && - attr.name.indexOf('xmlns:' + assertionSignature.prefix) === -1 && - xpath.select("//*[local-name(.)='Assertion']//*[namespace-uri(.)='" + attr.value + "'] or //*[local-name(.)='Assertion']//@*[namespace-uri(.)='" + attr.value + "']", samlResponse)) { - assertion.setAttribute(attr.name, attr.value); + const length = responseXML.attributes.length; + for (let i = 0; i < length; ++i) { + const attr = responseXML.attributes[i]; + // If attribute is a namespace, and is the signature prefix and is used in Assertion, copy it to assertion + // Don't set attributes that already exist (xmldom may copy them depending on the version) + if (!assertionDom.getAttribute(attr.name)) { + continue + } + const select = encrypted ? + "/*[local-name(.)='Assertion'][1]//*[namespace-uri(.)='" + attr.value + "'] or /*[local-name(.)='Assertion'][1]//@*[namespace-uri(.)='" + attr.value + "']" : + "/*[local-name(.)='Response'][1]/*[local-name(.)='Assertion'][1]//*[namespace-uri(.)='" + attr.value + "'] or /*[local-name(.)='Response'][1]/*[local-name(.)='Assertion'][1]//@*[namespace-uri(.)='" + attr.value + "']"; + if (attr.name.indexOf('xmlns') === 0 && + attr.name.indexOf('xmlns:' + assertionSignature.prefix) === -1 && + xpath.select(select, responseXML)) { + assertionDom.setAttribute(attr.name, attr.value); } } } } if (isResponseSigned) { - self._saml.validateSignature(samlResponse, { - cert: self.options.cert, - thumbprints: self.options.thumbprints, + this._saml.validateSignature(samlResponseStr, { + meta: meta, signaturePath: samlResponseSignaturePath - }, - function (err) { + }, (err, signed) => { if (err) { return callback(err); } - if (!isAssertionSigned) { - return self._saml.parseAssertion(assertion, callback); - } - - return self._saml.validateSamlAssertion(assertion, callback); + this.extractAssertion(signed, (err, assertion) => { + if (err) { + // shouldn't happen + return callback(err); + } + if (!assertion) { + return callback(new Error('saml response does not contain an Assertion element (Status: ' + samlStatus.code + ')')); + } + // no need to validate the assertion once again due: + // In parseAssertion, it decrypts the EncryptedAssertion from solely the signed string. Since the encrypted cipher text is signed via the response element, i.e. a subset, it's integrity is also protected. + //Even if the underlying Encrypted Assertion post-decryption has a Signature, we don't need to verify it, because the cipher text was already protected + return this._saml.parseAssertion(assertion, callback); + }); }); } else if (isAssertionSigned) { - return self._saml.validateSamlAssertion(assertion, callback); + return this._saml.validateSamlAssertion(assertionStr || samlResponseStr, { meta }, callback); } }); }); }); } -}; - -function generateInstant() { - var date = new Date(); - return date.getUTCFullYear() + '-' + ('0' + (date.getUTCMonth()+1)).slice(-2) + '-' + ('0' + date.getUTCDate()).slice(-2) + 'T' + ('0' + date.getUTCHours()).slice(-2) + ":" + ('0' + date.getUTCMinutes()).slice(-2) + ":" + ('0' + date.getUTCSeconds()).slice(-2) + "Z"; } -function stripQueryAndFragmentFromURL(url) { - return url.split("#")[0].split("?")[0]; -} +module.exports = Samlp; \ No newline at end of file
lib/passport-wsfed-saml2/state/session.js+13 −9 modified@@ -1,4 +1,4 @@ -var uid = require('uid2'); +const uid = require('uid2'); /** * Creates an instance of `SessionStore`. @@ -38,11 +38,15 @@ function SessionStore(options) { SessionStore.prototype.store = function(req, callback) { if (!req.session) { return callback(new Error('Authentication requires session support when using state. Did you forget to use express-session middleware?')); } - var key = this._key; - var state = uid(24); - if (!req.session[key]) { req.session[key] = {}; } - req.session[key].state = state; - callback(null, state); + const key = this._key; + uid(24, (err, state) => { + if (err) { + return callback(err); + } + if (!req.session[key]) { req.session[key] = {}; } + req.session[key].state = state; + callback(null, state); + }); }; /** @@ -59,12 +63,12 @@ SessionStore.prototype.store = function(req, callback) { SessionStore.prototype.verify = function(req, providedState, callback) { if (!req.session) { return callback(new Error('Authentication requires session support when using state. Did you forget to use express-session middleware?')); } - var key = this._key; + const key = this._key; if (!req.session[key]) { return callback(null, false, { message: 'Unable to verify authorization request state.' }); } - var state = req.session[key].state; + const state = req.session[key].state; if (!state) { return callback(null, false, { message: 'Unable to verify authorization request state.' }); } @@ -82,4 +86,4 @@ SessionStore.prototype.verify = function(req, providedState, callback) { }; // Expose constructor. -module.exports = SessionStore; +module.exports = SessionStore; \ No newline at end of file
lib/passport-wsfed-saml2/strategy.js+25 −30 modified@@ -1,17 +1,15 @@ -var util = require('util'); -var url = require('url'); -var jwt = require('jsonwebtoken'); -var Strategy = require('passport-strategy'); -var saml = require('./saml'); -var wsfed = require('./wsfederation'); -var samlp = require('./samlp'); -var getReqUrl = require('./utils').getReqUrl; -var EventEmitter = require('events'); -var utils = require('./utils'); - -var utils = require('./utils'); -var NullStateStore = require('./state/null'); -var SessionStateStore = require('./state/session'); +const util = require('util'); +const url = require('url'); +const jwt = require('jsonwebtoken'); +const Strategy = require('passport-strategy'); +const saml = require('./saml'); +const wsfed = require('./wsfederation'); +const samlp = require('./samlp'); +const getReqUrl = require('./utils').getReqUrl; +const EventEmitter = require('events'); +const utils = require('./utils'); +const NullStateStore = require('./state/null'); +const SessionStateStore = require('./state/session'); function WsFedSaml2Strategy (options, verify) { if (typeof options === 'function') { @@ -36,7 +34,7 @@ function WsFedSaml2Strategy (options, verify) { if (!this.options.jwt) { this._saml = new saml.SAML(this.options); - this._samlp = new samlp(this.options, this._saml); + this._samlp = new samlp(this.options, this._saml); } else { this._jwt = this.options.jwt; } @@ -63,12 +61,12 @@ util.inherits(WsFedSaml2Strategy, Strategy); WsFedSaml2Strategy.prototype._authenticate_saml = function (req, state) { var self = this; - self._wsfed.retrieveToken(req, function(err, token) { + self._wsfed.retrieveToken(req, function(err, wResult) { if (err) return self.fail(err, err.status || 400); self.options.recipientUrl = self.options.recipientUrl || getReqUrl(req); - self._saml.validateSamlAssertion(token, function (err, profile) { + self._saml.validateSamlAssertion(wResult, { meta: { req } }, function (err, profile) { if (err) { return self.error(err); } @@ -94,7 +92,6 @@ WsFedSaml2Strategy.prototype._authenticate_saml = function (req, state) { } }) }); - }; WsFedSaml2Strategy.prototype._authenticate_jwt = function (req, state) { @@ -213,15 +210,13 @@ WsFedSaml2Strategy.prototype.authenticate = function (req, opts) { return self.fail('SAMLResponse should be a valid xml', 400); } - var samlResponseDom = utils.parseSamlResponse(samlResponse); - // If options are not set, we set the expected value from the request object var req_full_url = getReqUrl(req); self.options.destinationUrl = self.options.destinationUrl || req_full_url; self.options.recipientUrl = self.options.recipientUrl || req_full_url; - self._samlp.validateSamlResponse(samlResponseDom, function (err, profile) { + self._samlp.validateSamlResponse(samlResponse, { meta: { req } }, function (err, profile) { if (err) return self.fail(err, err.status || 400); var verified = function (err, user, info) { @@ -282,19 +277,19 @@ WsFedSaml2Strategy.prototype.authenticate = function (req, opts) { } switch (protocol) { - case 'wsfed': - executeWsfed(req, this.options); - break; - case 'samlp': - executeSamlp(req, this.options); - break; - default: - throw new Error('not supported protocol: ' + protocol); + case 'wsfed': + executeWsfed(req, this.options); + break; + case 'samlp': + executeSamlp(req, this.options); + break; + default: + throw new Error('not supported protocol: ' + protocol); } }; WsFedSaml2Strategy.prototype.authorizationParams = function(options) { return options; }; -module.exports = WsFedSaml2Strategy; +module.exports = WsFedSaml2Strategy; \ No newline at end of file
lib/passport-wsfed-saml2/utils.js+42 −20 modified@@ -1,20 +1,29 @@ -var xmldom = require('@auth0/xmldom'); -var crypto = require('crypto'); +const xmldom = require('@xmldom/xmldom'); -var SamlAssertionParserError = require('./errors/SamlAssertionParserError'); -var SamlResponseParserError = require('./errors/SamlResponseParserError'); -var WSFederationResultParserError = require('./errors/WSFederationResultParserError'); +const crypto = require('crypto'); -const CERT_START = "-----BEGIN CERTIFICATE-----\n"; -const CERT_END = "\n-----END CERTIFICATE-----\n"; +const SamlAssertionParserError = require('./errors/SamlAssertionParserError'); +const SamlResponseParserError = require('./errors/SamlResponseParserError'); +const SamlRequestParserError = require('./errors/SamlRequestParserError'); +const WSFederationResultParserError = require('./errors/WSFederationResultParserError'); + +const CERT_START = '-----BEGIN CERTIFICATE-----\n'; +const CERT_END = '\n-----END CERTIFICATE-----\n'; exports.certToPEM = (cert) => CERT_START + cert.match(/.{1,64}/g).join('\n') + CERT_END; // convert from \r\n -> \n this should be done by the xml parser, but is ignoring this. -function crlf2lf(string) { +exports.crlf2lf = function crlf2lf(string) { return string.replace(/\r\n?/g, '\n'); } +exports.parseXmlString = function(xml, parser = new xmldom.DOMParser()) { + const normalizedString = exports.crlf2lf(xml); + // to avoid a breaking change for customers, return the original result. + return parser.parseFromString(normalizedString, 'text/xml'); +} + + exports.getSamlAssertionVersion = function(samlAssertion){ if (samlAssertion.getAttribute('MajorVersion') === '1') { return '1.1'; @@ -24,13 +33,13 @@ exports.getSamlAssertionVersion = function(samlAssertion){ // In this case the version is undefined, or we weren't able to determine it. return undefined; } - }; -exports.parseSamlAssertion = function(xml) { + +exports.parseSamlAssertion = function(xml, parser) { if (typeof xml === 'string') { try { - return new xmldom.DOMParser().parseFromString(crlf2lf(xml)); + return exports.parseXmlString(xml, parser); } catch (e) { throw new SamlAssertionParserError('SAML Assertion should be a valid xml', e); } @@ -39,10 +48,10 @@ exports.parseSamlAssertion = function(xml) { return xml; }; -exports.parseSamlResponse = function(xml) { +exports.parseSamlResponse = function(xml, parser) { if (typeof xml === 'string') { try { - return new xmldom.DOMParser().parseFromString(crlf2lf(xml)); + return exports.parseXmlString(xml, parser); } catch (e) { throw new SamlResponseParserError('SAMLResponse should be a valid xml', e); } @@ -51,10 +60,23 @@ exports.parseSamlResponse = function(xml) { return xml; }; -exports.parseWsFedResponse = function(xml) { + +exports.parseSamlRequest = function(xml, parser) { if (typeof xml === 'string') { try { - return new xmldom.DOMParser().parseFromString(crlf2lf(xml)); + return exports.parseXmlString(xml, parser); + } catch (e) { + throw new SamlRequestParserError('SAMLRequest should be a valid xml', e); + } + } + + return xml; +}; + +exports.parseWsFedResponse = function(xml, parser) { + if (typeof xml === 'string') { + try { + return exports.parseXmlString(xml, parser); } catch (e) { throw new WSFederationResultParserError('wresult should be a valid xml', e); } @@ -68,15 +90,15 @@ exports.getReqUrl = function(req){ }; exports.generateUniqueID = function() { - var uniqueID = crypto.randomBytes(16); + const uniqueID = crypto.randomBytes(16); return uniqueID.toString('hex'); }; exports.getEncoding = function(xml){ - try{ - const response = new xmldom.DOMParser().parseFromString(crlf2lf(xml)); + try { + const response = exports.parseXmlString(xml); // <?xml version="1.0" encoding="XXXX"?> -> read encoding - if (response.firstChild && response.firstChild.tagName == 'xml'){ + if (response.firstChild && response.firstChild.nodeName === 'xml'){ const regex = /(?:encoding=\")([^\"]*)(?:\")/g; const match = regex.exec(response.firstChild.nodeValue); // [0] the complete match @@ -110,4 +132,4 @@ exports.stringCompare = function(a,b) { } return a === b; -}; +}; \ No newline at end of file
lib/passport-wsfed-saml2/wsfederation.js+32 −23 modified@@ -1,20 +1,25 @@ -var xtend = require('xtend'); -var qs = require('querystring'); -var xpath = require('xpath'); +const xtend = require('xtend'); +const qs = require('querystring'); +const xpath = require('xpath'); +const xmldom = require('@xmldom/xmldom'); -var utils = require('./utils'); -var AuthenticationFailedError = require('./errors/AuthenticationFailedError'); +const domParser = new xmldom.DOMParser(); -var WsFederation = module.exports = function WsFederation (realm, homerealm, identityProviderUrl, wreply) { +const utils = require('./utils'); +const AuthenticationFailedError = require('./errors/AuthenticationFailedError'); + + +const WsFederation = module.exports = function WsFederation (realm, homerealm, identityProviderUrl, wreply) { this.realm = realm; this.homerealm = homerealm; this.identityProviderUrl = identityProviderUrl; this.wreply = wreply; + this.parser = domParser; }; WsFederation.prototype = { getRequestSecurityTokenUrl: function (options) { - var query = xtend(options || {}, { + const query = xtend(options || {}, { wtrealm: this.realm, wa: 'wsignin1.0' }); @@ -31,13 +36,13 @@ WsFederation.prototype = { }, extractToken: function(req) { - var doc = utils.parseWsFedResponse(req.body['wresult']); + const doc = utils.parseWsFedResponse(req.body['wresult'], this.parser); // //Probe WS-Trust 1.2 namespace (http://schemas.xmlsoap.org/ws/2005/02/trust) - var token = doc.getElementsByTagNameNS('http://schemas.xmlsoap.org/ws/2005/02/trust', 'RequestedSecurityToken')[0]; + let token = doc.getElementsByTagNameNS('http://schemas.xmlsoap.org/ws/2005/02/trust', 'RequestedSecurityToken')[0]; // //Probe WS-Trust 1.3 namespace (http://docs.oasis-open.org/ws-sx/ws-trust/200512) - if(!token){ + if (!token) { token = doc.getElementsByTagNameNS('http://docs.oasis-open.org/ws-sx/ws-trust/200512', 'RequestedSecurityToken')[0]; } @@ -48,51 +53,55 @@ WsFederation.prototype = { if (req.body.wresult.indexOf('<') === -1) { return callback(new Error('wresult should be a valid xml')); } - - var fault = this.extractFault(req); + const fault = this.extractFault(req); if (fault) { return callback(new AuthenticationFailedError(fault.message, fault.detail)); } - var token = this.extractToken(req); + const token = this.extractToken(req); if (!token) { return callback(new Error('missing RequestedSecurityToken element')); } // Check for more than one Assertions to conform with spec - var foundAssertions = xpath.select("//*[local-name(.)='Assertion']", token); + const foundAssertions = xpath.select("//*[local-name(.)='Assertion']", token); if (foundAssertions.length > 1) { return callback(new Error('A RequestedSecurityToken can contain only one Assertion element.')); } - callback(null, token); + callback(null, req.body.wresult); }, extractFault: function(req) { - var fault = {}; - var doc = utils.parseWsFedResponse(req.body['wresult']); + const fault = {}; + let doc; + try { + doc = utils.parseWsFedResponse(req.body['wresult'], this.parser); + } catch (err) { + return err; + } - var isFault = xpath.select("//*[local-name(.)='Fault']", doc)[0]; + const isFault = xpath.select("//*[local-name(.)='Fault']", doc)[0]; if (!isFault) { return null; } - var codeXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Code']/*[local-name(.)='Value']", doc)[0]; + const codeXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Code']/*[local-name(.)='Value']", doc)[0]; if (codeXml) { fault.code = codeXml.textContent; } - var subCodeXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Code']/*[local-name(.)='Subcode']/*[local-name(.)='Value']", doc)[0]; + const subCodeXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Code']/*[local-name(.)='Subcode']/*[local-name(.)='Value']", doc)[0]; if (subCodeXml) { fault.subCode = subCodeXml.textContent; } - var messageXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Reason']/*[local-name(.)='Text']", doc)[0]; + const messageXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Reason']/*[local-name(.)='Text']", doc)[0]; if (messageXml) { fault.message = messageXml.textContent; } - var detailXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Detail']", doc)[0]; + const detailXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Detail']", doc)[0]; if (detailXml) { fault.detail = detailXml.textContent; } @@ -117,4 +126,4 @@ Object.defineProperty(WsFederation, 'identityProviderUrl', { get: function () { return this.identityProviderUrl; } -}); +}); \ No newline at end of file
package.json+8 −8 modified@@ -1,6 +1,6 @@ { "name": "passport-wsfed-saml2", - "version": "4.6.3", + "version": "4.6.4", "description": "SAML2 Protocol and WS-Fed library", "scripts": { "test": "./node_modules/.bin/mocha --recursive", @@ -21,16 +21,16 @@ }, "main": "./lib/passport-wsfed-saml2", "dependencies": { - "@auth0/xmldom": "0.1.22", - "ejs": "2.5.5", + "@xmldom/xmldom": "^0.9.8", + "ejs": "^3.1.10", "jsonwebtoken": "~9.0.0", - "node-forge": "^0.10.0", + "node-forge": "^1.3.1", "passport-strategy": "^1.0.0", - "uid2": "0.0.x", + "uid2": "^1.0.0", "valid-url": "^1.0.9", - "xml-crypto": "auth0/xml-crypto#v1.4.1-auth0.2", + "xml-crypto": "^6.1.0", "xml-encryption": "^2.0.0", - "xpath": "0.0.5", + "xpath": "^0.0.33", "xtend": "~2.0.3" }, "devDependencies": { @@ -47,7 +47,7 @@ "wsfed": "~0.3.5" }, "engines": { - "node": ">= 12" + "node": ">= 18" }, "licenses": [ {
test/fixture/samlp-server.js+352 −340 modified@@ -8,425 +8,437 @@ var path = require('path'); var passport = require('passport'); var Strategy = require('../../lib/passport-wsfed-saml2').Strategy; -var identityProviderUrl = 'http://localhost:5051/samlp'; +const PORT = 3000 + Math.floor(Math.random() * 7000); +const BASE_URL = `http://localhost:${PORT}`; +var identityProviderUrl = `${BASE_URL}/samlp`; var relayState = 'somestate'; +passport.serializeUser(function(user, done) { + done(null, user); +}); + +passport.deserializeUser(function(user, done) { + done(null, user); +}); + passport.use('samlp', new Strategy({ - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: identityProviderUrl, - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - recipientUrl: 'https://auth0-dev-ed.my.salesforce.com', - destinationUrl: 'https://auth0-dev-ed.my.salesforce.com' - }, function(profile, done) { - return done(null, profile); - }) + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + recipientUrl: 'https://auth0-dev-ed.my.salesforce.com', + destinationUrl: 'https://auth0-dev-ed.my.salesforce.com' + }, function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-http-post', new Strategy({ - protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: identityProviderUrl, - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'] - }, function(profile, done) { - return done(null, profile); - }) + protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'] + }, function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-custom-request-template', new Strategy({ - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: identityProviderUrl, - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - requestTemplate: '<AuthnRequest Issuertico="@@Issuer@@" Version="3.0" Protocol="@@ProtocolBinding@@" Foo="@@Foo.Test@@"></AuthnRequest>', - requestContext: { - Foo: { - Test: 123 - } - } - }, function(profile, done) { - return done(null, profile); - }) + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + requestTemplate: '<AuthnRequest Issuertico="@@Issuer@@" Version="3.0" Protocol="@@ProtocolBinding@@" Foo="@@Foo.Test@@"></AuthnRequest>', + requestContext: { + Foo: { + Test: 123 + } + } + }, function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-idpurl-with-querystring', new Strategy( - { - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: identityProviderUrl + '?foo=bar', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'] - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl + '?foo=bar', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'] + }, + function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-signedrequest-without-deflate', new Strategy({ - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: identityProviderUrl, - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - signingKey: { - key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), - cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), - }, - deflate: false - }, function(profile, done) { - return done(null, profile); - }) + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + signingKey: { + key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), + cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), + }, + deflate: false + }, function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-signedrequest-with-deflate', new Strategy({ - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: identityProviderUrl, - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - signingKey: { - key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), - cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), - }, - deflate: true - }, function(profile, done) { - return done(null, profile); - }) + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + signingKey: { + key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), + cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), + }, + deflate: true + }, function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-signedrequest-post', new Strategy({ - protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: identityProviderUrl, - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - signingKey: { - key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), - cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), - }, - deflate: false - }, function(profile, done) { - return done(null, profile); - }) + protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + signingKey: { + key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), + cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), + }, + deflate: false + }, function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-signedresponse', new Strategy( - { - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: identityProviderUrl, - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - recipientUrl: 'https://auth0-dev-ed.my.salesforce.com', - destinationUrl: 'https://auth0-dev-ed.my.salesforce.com' - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + recipientUrl: 'https://auth0-dev-ed.my.salesforce.com', + destinationUrl: 'https://auth0-dev-ed.my.salesforce.com' + }, + function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-signedresponse-invalidcert', new Strategy( - { - path: '/callback', - realm: 'urn:fixture-test', - identityProviderUrl: identityProviderUrl, - thumbprints: ['11111111111111111a5b93a43572eb2376fed309'], - recipientUrl: 'https://auth0-dev-ed.my.salesforce.com', - destinationUrl: 'https://auth0-dev-ed.my.salesforce.com' - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: identityProviderUrl, + thumbprints: ['11111111111111111a5b93a43572eb2376fed309'], + recipientUrl: 'https://auth0-dev-ed.my.salesforce.com', + destinationUrl: 'https://auth0-dev-ed.my.salesforce.com' + }, + function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-invalidcert', new Strategy( - { - path: '/callback', - realm: 'urn:fixture-test', - identityProviderUrl: identityProviderUrl, - thumbprints: ['11111111111111111a5b93a43572eb2376fed309'] - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: identityProviderUrl, + thumbprints: ['11111111111111111a5b93a43572eb2376fed309'] + }, + function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-signedresponse-signedassertion', new Strategy( - { - path: '/callback', - realm: 'urn:auth0:login-dev3', - thumbprints: ['C9ED4DFB07CAF13FC21E0FEC1572047EB8A7A4CB'], - destinationUrl: 'https://login-dev3.auth0.com:3000/login/callback', - recipientUrl: 'https://login-dev3.auth0.com:3000/login/callback', - checkExpiration: false // we are using a precomputed assertion generated from a sample idp feide - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + realm: 'urn:auth0:login-dev3', + thumbprints: ['C9ED4DFB07CAF13FC21E0FEC1572047EB8A7A4CB'], + destinationUrl: 'https://login-dev3.auth0.com:3000/login/callback', + recipientUrl: 'https://login-dev3.auth0.com:3000/login/callback', + checkExpiration: false // we are using a precomputed assertion generated from a sample idp feide + }, + function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-ping', new Strategy( - { - path: '/callback', - realm: 'urn:auth0:login-dev3', - thumbprints: ['44340220770a348444be34970939cff8a2d74f08'], - destinationUrl: 'https://login-dev3.auth0.com:3000/login/callback', - recipientUrl: 'https://login-dev3.auth0.com:3000/login/callback', - checkExpiration: false // we are using a precomputed assertion generated from a sample idp feide - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + realm: 'urn:auth0:login-dev3', + thumbprints: ['44340220770a348444be34970939cff8a2d74f08'], + destinationUrl: 'https://login-dev3.auth0.com:3000/login/callback', + recipientUrl: 'https://login-dev3.auth0.com:3000/login/callback', + checkExpiration: false // we are using a precomputed assertion generated from a sample idp feide + }, + function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-okta', new Strategy( - { - path: '/callback', - realm: 'https://auth0145.auth0.com', - thumbprints: ['a0c7dbb790e3476d3c5dd236f9f2060b1fd6e253'], - destinationUrl: 'https://auth0145.auth0.com', - recipientUrl: 'https://auth0145.auth0.com', - checkExpiration: false // we are using a precomputed assertion generated from a sample idp feide - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + realm: 'https://auth0145.auth0.com', + thumbprints: ['a0c7dbb790e3476d3c5dd236f9f2060b1fd6e253'], + destinationUrl: 'https://auth0145.auth0.com', + recipientUrl: 'https://auth0145.auth0.com', + checkExpiration: false // we are using a precomputed assertion generated from a sample idp feide + }, + function(profile, done) { + return done(null, profile); + }) ); function pemToCert(pem) { - // if certificate doesn't have ---- begin cert --- just return the pem - if (!/-----BEGIN CERTIFICATE-----/.test(pem.toString())) { - return pem.toString(); - } + // if certificate doesn't have ---- begin cert --- just return the pem + if (!/-----BEGIN CERTIFICATE-----/.test(pem.toString())) { + return pem.toString(); + } - var cert = /-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----/g.exec(pem.toString()); - if (cert.length > 0) { - return cert[1].replace(/[\n|\r\n]/g, ''); - } + var cert = /-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----/g.exec(pem.toString()); + if (cert.length > 0) { + return cert[1].replace(/[\n|\r\n]/g, ''); + } - return null; + return null; } passport.use('samlp-with-utf8', new Strategy( - { - path: '/callback', - thumbprints: ['119B9E027959CDB7C662CFD075D9E2EF384E445F'], - decryptionKey: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), - recipientUrl: 'https://login0.myauth0.com/login/callback', - destinationUrl: 'https://login0.myauth0.com/login/callback', - checkExpiration: false, // we are using a precomputed assertion generated from a sample idp feide - checkSPNameQualifier: false, - checkAudience: false - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + thumbprints: ['119B9E027959CDB7C662CFD075D9E2EF384E445F'], + decryptionKey: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), + recipientUrl: 'https://login0.myauth0.com/login/callback', + destinationUrl: 'https://login0.myauth0.com/login/callback', + checkExpiration: false, // we are using a precomputed assertion generated from a sample idp feide + checkSPNameQualifier: false, + checkAudience: false + }, + function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-with-ISO', new Strategy( - { - path: '/callback', - cert: pemToCert(fs.readFileSync(path.join(__dirname, '../test-auth0.pem'))), - checkExpiration: false, // we are using a precomputed assertion generated from a sample idp feide - checkAudience: false, - checkDestination: false, - checkRecipient: false - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + cert: pemToCert(fs.readFileSync(path.join(__dirname, '../test-auth0.pem'))), + checkExpiration: false, // we are using a precomputed assertion generated from a sample idp feide + checkAudience: false, + checkDestination: false, + checkRecipient: false + }, + function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-with-ISO-explicit', new Strategy( - { - path: '/callback', - cert: pemToCert(fs.readFileSync(path.join(__dirname, '../test-auth0.pem'))), - checkExpiration: false, // we are using a precomputed assertion generated from a sample idp feide - checkAudience: false, - default_encoding: 'ISO-8859-1', - checkDestination: false, - checkRecipient: false - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + cert: pemToCert(fs.readFileSync(path.join(__dirname, '../test-auth0.pem'))), + checkExpiration: false, // we are using a precomputed assertion generated from a sample idp feide + checkAudience: false, + default_encoding: 'ISO-8859-1', + checkDestination: false, + checkRecipient: false + }, + function(profile, done) { + return done(null, profile); + }) ); passport.use('samlp-with-dsig-at-root', new Strategy( - { - path: '/callback', - checkExpiration: false, // we are using a precomputed assertion generated from a sample idp - checkAudience: false, - checkDestination: false, - cert: pemToCert(fs.readFileSync(path.join(__dirname, '../test-auth0.pem'))) - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + checkExpiration: false, // we are using a precomputed assertion generated from a sample idp + checkAudience: false, + checkDestination: false, + cert: pemToCert(fs.readFileSync(path.join(__dirname, '../test-auth0.pem'))) + }, + function(profile, done) { + return done(null, profile); + }) ); var fakeUser = { - id: '12345678', - displayName: 'John Foo', - name: { - familyName: 'Foo', - givenName: 'John' - }, - emails: [ - { - type: 'work', - value: 'jfoo@gmail.com' - } - ] + id: '12345678', + displayName: 'John Foo', + name: { + familyName: 'Foo', + givenName: 'John' + }, + emails: [ + { + type: 'work', + value: 'jfoo@gmail.com' + } + ] }; var credentials = { - cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), - key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')) + cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), + key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')) }; module.exports.options = {}; module.exports.start = function(options, callback){ - module.exports.options = options; - if (typeof options === 'function') { - callback = options; - module.exports.options = {}; - } - - var app = express(); - - app.configure(function(){ - this.use(express.bodyParser()); - this.use(passport.initialize()); - this.use(passport.session()); - this.use(function(req,res,next){ - req.user = fakeUser; - next(); - }); - }); - - function getPostURL (audience, samlRequestDom, req, callback) { - callback(null, 'http://localhost:5051/callback'); - } - - //configure samlp middleware - app.get('/samlp', function(req, res, next) { - samlp.auth(xtend({}, { - issuer: 'urn:fixture-test', - getPostURL: getPostURL, - cert: credentials.cert, - key: credentials.key, - recipient: 'https://auth0-dev-ed.my.salesforce.com' - }, module.exports.options))(req, res); - }); - - app.get('/login', passport.authenticate('samlp', { protocol: 'samlp', RelayState: relayState })); - app.get('/login-http-post', passport.authenticate('samlp-http-post', { protocol: 'samlp', RelayState: relayState })); - app.get('/login-idp-with-querystring', passport.authenticate('samlp-idpurl-with-querystring', { protocol: 'samlp', RelayState: relayState })); - - app.get('/login-signed-request-without-deflate', passport.authenticate('samlp-signedrequest-without-deflate', { protocol: 'samlp', RelayState: relayState })); - app.get('/login-signed-request-post', passport.authenticate('samlp-signedrequest-post', { protocol: 'samlp', RelayState: relayState })); - app.get('/login-signed-request-with-deflate', passport.authenticate('samlp-signedrequest-with-deflate', { protocol: 'samlp', RelayState: relayState })); - - app.get('/login-custom-request-template', - passport.authenticate('samlp-custom-request-template', { protocol: 'samlp', RelayState: relayState })); - - app.post('/callback', - function(req, res, next) { - next(); - }, - passport.authenticate('samlp', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); + module.exports.options = options; + if (typeof options === 'function') { + callback = options; + module.exports.options = {}; } - ); - app.post('/callback/samlp-signedresponse', - passport.authenticate('samlp-signedresponse', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); - } - ); - - app.post('/callback/samlp-signedresponse-invalidcert', - passport.authenticate('samlp-signedresponse-invalidcert', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); - } - ); - - app.post('/callback/samlp-invalidcert', - passport.authenticate('samlp-invalidcert', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); - } - ); - - app.post('/callback/samlp-signedresponse-signedassertion', - passport.authenticate('samlp-signedresponse-signedassertion', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); - } - ); + var app = express(); - app.post('/callback/samlp-ping', - passport.authenticate('samlp-ping', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); - } - ); - - app.post('/callback/samlp-okta', - passport.authenticate('samlp-okta', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); - } - ); - - app.post('/callback/samlp-with-utf8', - passport.authenticate('samlp-with-utf8', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); - } - ); - - app.post('/callback/samlp-with-ISO', - passport.authenticate('samlp-with-ISO', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); - } - ); - - app.post('/callback/samlp-with-ISO-explicit', - passport.authenticate('samlp-with-ISO-explicit', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); - } -); + app.configure(function(){ + this.use(express.bodyParser()); + this.use(passport.initialize()); + this.use(passport.session()); + this.use(function(req,res,next){ + req.user = fakeUser; + next(); + }); + }); - app.post('/callback/samlp-with-invalid-xml', - function (req, res, next) { - passport.authenticate('samlp-with-utf8', { protocol: 'samlp' }, function(err, user, info) { - res.send(400, { message: err.message }); - })(req, res, next); - }, - function(req, res) { - res.json(req.user); + function getPostURL (audience, samlRequestDom, req, callback) { + callback(null, `${BASE_URL}/callback`); } - ); - app.post('/callback/samlp-with-dsig-at-root', - passport.authenticate('samlp-with-dsig-at-root', { protocol: 'samlp' }), - function(req, res) { - res.json(req.user); - } - ); + //configure samlp middleware + app.get('/samlp', function(req, res, next) { + samlp.auth(xtend({}, { + issuer: 'urn:fixture-test', + getPostURL: getPostURL, + cert: credentials.cert, + key: credentials.key, + recipient: 'https://auth0-dev-ed.my.salesforce.com' + }, module.exports.options))(req, res); + }); - var server = http.createServer(app).listen(5051, callback); - module.exports.close = server.close.bind(server); + app.get('/login', passport.authenticate('samlp', { protocol: 'samlp', RelayState: relayState })); + app.get('/login-http-post', passport.authenticate('samlp-http-post', { protocol: 'samlp', RelayState: relayState })); + app.get('/login-idp-with-querystring', passport.authenticate('samlp-idpurl-with-querystring', { protocol: 'samlp', RelayState: relayState })); + + app.get('/login-signed-request-without-deflate', passport.authenticate('samlp-signedrequest-without-deflate', { protocol: 'samlp', RelayState: relayState })); + app.get('/login-signed-request-post', passport.authenticate('samlp-signedrequest-post', { protocol: 'samlp', RelayState: relayState })); + app.get('/login-signed-request-with-deflate', passport.authenticate('samlp-signedrequest-with-deflate', { protocol: 'samlp', RelayState: relayState })); + + app.get('/login-custom-request-template', + passport.authenticate('samlp-custom-request-template', { protocol: 'samlp', RelayState: relayState })); + + app.post('/callback', + function(req, res, next) { + next(); + }, + passport.authenticate('samlp', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-signedresponse', + passport.authenticate('samlp-signedresponse', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-signedresponse-invalidcert', + passport.authenticate('samlp-signedresponse-invalidcert', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-invalidcert', + passport.authenticate('samlp-invalidcert', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-signedresponse-signedassertion', + passport.authenticate('samlp-signedresponse-signedassertion', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-ping', + passport.authenticate('samlp-ping', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-okta', + passport.authenticate('samlp-okta', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-with-utf8', + passport.authenticate('samlp-with-utf8', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-with-ISO', + passport.authenticate('samlp-with-ISO', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-with-ISO-explicit', + passport.authenticate('samlp-with-ISO-explicit', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-with-invalid-xml', + function (req, res, next) { + passport.authenticate('samlp-with-utf8', { protocol: 'samlp' }, function(err, user, info) { + res.send(400, { message: err.message }); + })(req, res, next); + }, + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-with-dsig-at-root', + passport.authenticate('samlp-with-dsig-at-root', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + var server = http.createServer(app).listen(PORT, callback); + module.exports.close = server.close.bind(server); }; module.exports.relayState = relayState; module.exports.identityProviderUrl = identityProviderUrl; module.exports.fakeUser = fakeUser; module.exports.credentials = credentials; +module.exports.BASE_URL = BASE_URL; +module.exports.PORT = PORT; \ No newline at end of file
test/fixture/wsfed-server.js+35 −29 modified@@ -8,16 +8,20 @@ var path = require('path'); var passport = require('passport'); var Strategy = require('../../lib/passport-wsfed-saml2').Strategy; + +const PORT = 3000 + Math.floor(Math.random() * 7000); +const BASE_URL = `http://localhost:${PORT}`; + passport.use(new Strategy( - { - path: '/callback', - realm: 'urn:fixture-test', - identityProviderUrl: 'http://localhost:5050/login', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'] - }, - function(profile, done) { - return done(null, profile); - }) + { + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: `${BASE_URL}/login`, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'] + }, + function(profile, done) { + return done(null, profile); + }) ); var fakeUser = { @@ -70,37 +74,39 @@ module.exports.start = function(options, callback){ }); function getPostURL (wtrealm, wreply, req, callback) { - callback(null, 'http://localhost:5050/callback'); + callback(null, `${BASE_URL}/callback`); } app.get('/login', - wsfed.auth(xtend({}, { - issuer: 'fixture-test', - getPostURL: getPostURL, - cert: credentials.cert, - key: credentials.key - }, options))); + wsfed.auth(xtend({}, { + issuer: 'fixture-test', + getPostURL: getPostURL, + cert: credentials.cert, + key: credentials.key + }, options))); app.post('/callback/wresult-with-invalid-xml', - function (req, res, next) { - passport.authenticate('wsfed-saml2', function(err, user, info) { - res.send(400, { message: err.message }); - })(req, res, next); - }, - function(req, res) { - res.json(req.user); - } + function (req, res, next) { + passport.authenticate('wsfed-saml2', function(err, user, info, status) { + res.send(400, { message: info.detail.message }); + })(req, res, next); + }, + function(req, res) { + res.json(req.user); + } ); app.post('/callback', - passport.authenticate('wsfed-saml2'), - function(req, res) { - res.json(req.user); - }); + passport.authenticate('wsfed-saml2'), + function(req, res) { + res.json(req.user); + }); - var server = http.createServer(app).listen(5050, callback); + var server = http.createServer(app).listen(PORT, callback); module.exports.close = server.close.bind(server); }; module.exports.fakeUser = fakeUser; module.exports.credentials = credentials; +module.exports.BASE_URL = BASE_URL; +module.exports.PORT = PORT; \ No newline at end of file
test/helpers.js+21 −29 modified@@ -1,65 +1,57 @@ -var xmlCrypto = require('xml-crypto'), - crypto = require('crypto'), - xmldom = require('@auth0/xmldom'); +const xmlCrypto = require('xml-crypto'); +const xmldom = require('@xmldom/xmldom'); +const xpath = require('xpath'); exports.isValidSignature = function(assertion, cert) { - var doc = new xmldom.DOMParser().parseFromString(assertion); - var signature = xmlCrypto.xpath(doc, "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0]; - var sig = new xmlCrypto.SignedXml(null, { idAttribute: 'AssertionID' }); - sig.keyInfoProvider = { - getKeyInfo: function (key) { - return "<X509Data></X509Data>"; - }, - getKey: function (keyInfo) { - return cert; - } - }; + var doc = new xmldom.DOMParser().parseFromString(assertion, 'text/xml'); + var signature = xpath.select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; + var sig = new xmlCrypto.SignedXml({ publicCert: cert, getCertFromKeyInfo: () => null, idAttribute: 'AssertionID' }); sig.loadSignature(signature.toString()); return sig.checkSignature(assertion); }; exports.getIssuer = function(assertion) { - var doc = new xmldom.DOMParser().parseFromString(assertion); + var doc = new xmldom.DOMParser().parseFromString(assertion, 'text/xml'); return doc.documentElement.getAttribute('Issuer'); }; exports.getAssertionID = function(assertion) { - var doc = new xmldom.DOMParser().parseFromString(assertion); + var doc = new xmldom.DOMParser().parseFromString(assertion, 'text/xml'); return doc.documentElement.getAttribute('AssertionID'); }; exports.getIssueInstant = function(assertion) { - var doc = new xmldom.DOMParser().parseFromString(assertion); + var doc = new xmldom.DOMParser().parseFromString(assertion, 'text/xml'); return doc.documentElement.getAttribute('IssueInstant'); }; exports.getConditions = function(assertion) { - var doc = new xmldom.DOMParser().parseFromString(assertion); + var doc = new xmldom.DOMParser().parseFromString(assertion, 'text/xml'); return doc.documentElement.getElementsByTagName('saml:Conditions'); }; exports.getAudiences = function(assertion) { - var doc = new xmldom.DOMParser().parseFromString(assertion); + var doc = new xmldom.DOMParser().parseFromString(assertion, 'text/xml'); return doc.documentElement - .getElementsByTagName('saml:Conditions')[0] - .getElementsByTagName('saml:AudienceRestrictionCondition')[0] - .getElementsByTagName('saml:Audience'); + .getElementsByTagName('saml:Conditions')[0] + .getElementsByTagName('saml:AudienceRestrictionCondition')[0] + .getElementsByTagName('saml:Audience'); }; exports.getAuthenticationStatement = function(assertion) { - var doc = new xmldom.DOMParser().parseFromString(assertion); + var doc = new xmldom.DOMParser().parseFromString(assertion, 'text/xml'); return doc.documentElement - .getElementsByTagName('saml:AuthenticationStatement')[0]; + .getElementsByTagName('saml:AuthenticationStatement')[0]; }; exports.getAttributes = function(assertion) { - var doc = new xmldom.DOMParser().parseFromString(assertion); + var doc = new xmldom.DOMParser().parseFromString(assertion, 'text/xml'); return doc.documentElement - .getElementsByTagName('saml:Attribute'); + .getElementsByTagName('saml:Attribute'); }; exports.getNameIdentifier = function(assertion) { - var doc = new xmldom.DOMParser().parseFromString(assertion); + var doc = new xmldom.DOMParser().parseFromString(assertion, 'text/xml'); return doc.documentElement - .getElementsByTagName('saml:NameIdentifier')[0]; -}; + .getElementsByTagName('saml:NameIdentifier')[0]; +}; \ No newline at end of file
test/interop.tests.js+46 −83 modified@@ -23,9 +23,9 @@ describe('interop', function () { var signedAssertion = '<Assertion ID="_1b1ffaef-86ef-42e1-92cf-cf8c9d9a4ce0" IssueInstant="2013-04-02T18:50:24.000Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/</Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /><ds:Reference URI="#_1b1ffaef-86ef-42e1-92cf-cf8c9d9a4ce0"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><ds:DigestValue>TzJmLs0BTPgpaPLsA7L2Kd9l1k4IBOmwIM/znV2iOPU=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>OHJCAffCNPRkwsE3RqnVPoCRSqsPrio8prABauzu2pqF418Y1QJuJehhzztY8A6kwnBUkBVE7BIyLe7kgCnBoNZWElYki1xtaLksc/Afc0TjlZvv9IJ9fQHIBiL1JA9KcySq1tu9dv/NauykBODXuljPuVTk6I4xLLWcg20o26Ov57axp42uWPpcJHtasomLmmmnAXEh6P7aB/1Vlm/MAJhWXToxacauJzFao3F9JNEuucKY6y3RPDp1Qq3vL0gq98RKuiaejayu6RjyyU2+8vCBzURul8b7ZXPUHfIOME6Q5LvbKqLhe/mzqRc+9GUg22X3B5SYjdnXjwHbBTbihA==</ds:SignatureValue><KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><X509Data><X509Certificate>MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng</X509Certificate></X509Data></KeyInfo></ds:Signature><Subject><NameID>10030000838D23AF@MicrosoftOnline.com</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" /></Subject><Conditions NotBefore="2013-04-02T18:50:23.969Z" NotOnOrAfter="2013-04-03T06:50:23.969Z"><AudienceRestriction><Audience>spn:408153f4-5960-43dc-9d4f-6b717d772c8d</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid"><AttributeValue>75696069-df44-4310-9bcf-08b45e3007c9</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"><AttributeValue>Matias</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><AttributeValue>matias@auth0.onmicrosoft.com</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"><AttributeValue>Woloski</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider"><AttributeValue>https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="2013-04-02T18:50:16.000Z"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion>'; var saml_passport = new SamlPassport({thumbprints: ['3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe0'], - realm: 'spn:408153f4-5960-43dc-9d4f-6b717d772c8d', - checkExpiration: false, checkRecipient: false}); // dont check expiration since we are harcoding the token - var profile = saml_passport.validateSamlAssertion(signedAssertion, function(err, profile) { + realm: 'spn:408153f4-5960-43dc-9d4f-6b717d772c8d', + checkExpiration: false, checkRecipient: false}); // dont check expiration since we are harcoding the token + var profile = saml_passport.validateSamlAssertion(signedAssertion, {}, function(err, profile) { if (err) return done(err); assert.ok(profile); done(); @@ -40,7 +40,7 @@ describe('interop', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback/samlp-signedresponse-signedassertion', + uri: `${server.BASE_URL}/callback/samlp-signedresponse-signedassertion`, form: { SAMLResponse: 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfZjEzOGQyZTUzMWQ0NjI0ZmNhZmQ4OGJlYWNmN2VjMzkwMzRmMmEzNzRkIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxMy0wNy0wN1QxMTo1NToxOFoiIERlc3RpbmF0aW9uPSJodHRwczovL2xvZ2luLWRldjMuYXV0aDAuY29tOjMwMDAvbG9naW4vY2FsbGJhY2siIEluUmVzcG9uc2VUbz0iX2ZkMDY3N2ExZmRmMTU0Y2JmZGQwIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9vcGVuaWRwLmZlaWRlLm5vPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KICA8ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPgogIDxkczpSZWZlcmVuY2UgVVJJPSIjX2YxMzhkMmU1MzFkNDYyNGZjYWZkODhiZWFjZjdlYzM5MDM0ZjJhMzc0ZCI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+VXJHUURDSGF0eTRjNzZqTW5oWmZZb09qQ1RFPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5uSGZLUDRzbXliTHQxRTdwNVZJMkttUnZtL3RYMEpVRVNGYUN6ejM4M1RDMWpTU2JaODZKSVJYSVdMRXl1WTJCOTJBNHdmdC8zaHhqV2ZBNTNWUFdsYS93UzBEcitRbzUxU2svTzZNek1tbXRXakx2WVZhTDhvQ3lZUFZHSDlyWXZ4cnlnVXFyVkZDZVZhS3U5Y1VwVWpPdXZTYzM1dUovOEJFZUZ1cTdBMm89PC9kczpTaWduYXR1cmVWYWx1ZT4KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ2l6Q0NBZlFDQ1FDWTh0S2FNYzBCTWpBTkJna3Foa2lHOXcwQkFRVUZBRENCaVRFTE1Ba0dBMVVFQmhNQ1RrOHhFakFRQmdOVkJBZ1RDVlJ5YjI1a2FHVnBiVEVRTUE0R0ExVUVDaE1IVlU1SlRrVlVWREVPTUF3R0ExVUVDeE1GUm1WcFpHVXhHVEFYQmdOVkJBTVRFRzl3Wlc1cFpIQXVabVZwWkdVdWJtOHhLVEFuQmdrcWhraUc5dzBCQ1FFV0dtRnVaSEpsWVhNdWMyOXNZbVZ5WjBCMWJtbHVaWFIwTG01dk1CNFhEVEE0TURVd09EQTVNakkwT0ZvWERUTTFNRGt5TXpBNU1qSTBPRm93Z1lreEN6QUpCZ05WQkFZVEFrNVBNUkl3RUFZRFZRUUlFd2xVY205dVpHaGxhVzB4RURBT0JnTlZCQW9UQjFWT1NVNUZWRlF4RGpBTUJnTlZCQXNUQlVabGFXUmxNUmt3RndZRFZRUURFeEJ2Y0dWdWFXUndMbVpsYVdSbExtNXZNU2t3SndZSktvWklodmNOQVFrQkZocGhibVJ5WldGekxuTnZiR0psY21kQWRXNXBibVYwZEM1dWJ6Q0JuekFOQmdrcWhraUc5dzBCQVFFRkFBT0JqUUF3Z1lrQ2dZRUF0OGpMb3FJMVZUbHhBWjJheGlESVRoV2NBT1hkdThLa1ZVV2FOL1Nvb085TzBRUTdLUlVqU0dLTjlKSzY1QUZSRFhRa1dQQXU0SGxuTzRub1lsRlNMbll5RHhJNjZMQ3I3MXg0bGdGSmpxTGVBdkIvR3FCcUZmSVozWUsvTnJoblVxRndadTYzbkxyWmpjVVp4TmFQak9PU1JTRGFYcHYxa2I1azNqT2lTR0VDQXdFQUFUQU5CZ2txaGtpRzl3MEJBUVVGQUFPQmdRQlFZajRjQWFmV2FZZmpCVTJ6aTFFbHdTdElhSjVueXAvcy84QjhTQVBLMlQ3OU1jTXljY1Azd1NXMTNMSGttTTFqd0tlM0FDRlhCdnFHUU4wSWJjSDQ5aHUwRktoWUZNL0dQREpjSUhGQnNpeU1CWENocHllOXZCYVRORUJDdFUzS2pqeUcwaFJUMm1BUTloK2JrUG1PdmxFby9hSDB4UjY4WjlodzRQRjEzdz09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9Il9lYzM1MzRjN2Y2NjYzMjdlNmFmMTU0MzdiZTdiODk5OTU4ZDMwZGY5NzUiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDEzLTA3LTA3VDExOjU1OjE4WiI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vb3BlbmlkcC5mZWlkZS5ubzwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICA8ZHM6UmVmZXJlbmNlIFVSST0iI19lYzM1MzRjN2Y2NjYzMjdlNmFmMTU0MzdiZTdiODk5OTU4ZDMwZGY5NzUiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPlZPWVNVQlZZSUNvTWJwbk5INEVCRHhBUWtKTT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+RW1rR1docVZvZ25vNWhja01UcG9ySHFwT0szVDZpZ2JRVXA2Zmkxc1pvcXFsd3cxSUtmc3REMW1LdzVjM21JcldyNjFnOTh4TFMxLzBnMW5hUWlpT0MzbDl6Y0g3QUFIOVdGWW5JejdGeUE4dmllKzBxTE1Dbno4cVVpZ21HWDNRbEdiQ1QzUHVUNDEzUWlZSm9DT2VXME5zYUpaWUNINUFOWnprSUJsdG9nPTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNpekNDQWZRQ0NRQ1k4dEthTWMwQk1qQU5CZ2txaGtpRzl3MEJBUVVGQURDQmlURUxNQWtHQTFVRUJoTUNUazh4RWpBUUJnTlZCQWdUQ1ZSeWIyNWthR1ZwYlRFUU1BNEdBMVVFQ2hNSFZVNUpUa1ZVVkRFT01Bd0dBMVVFQ3hNRlJtVnBaR1V4R1RBWEJnTlZCQU1URUc5d1pXNXBaSEF1Wm1WcFpHVXVibTh4S1RBbkJna3Foa2lHOXcwQkNRRVdHbUZ1WkhKbFlYTXVjMjlzWW1WeVowQjFibWx1WlhSMExtNXZNQjRYRFRBNE1EVXdPREE1TWpJME9Gb1hEVE0xTURreU16QTVNakkwT0Zvd2dZa3hDekFKQmdOVkJBWVRBazVQTVJJd0VBWURWUVFJRXdsVWNtOXVaR2hsYVcweEVEQU9CZ05WQkFvVEIxVk9TVTVGVkZReERqQU1CZ05WQkFzVEJVWmxhV1JsTVJrd0Z3WURWUVFERXhCdmNHVnVhV1J3TG1abGFXUmxMbTV2TVNrd0p3WUpLb1pJaHZjTkFRa0JGaHBoYm1SeVpXRnpMbk52YkdKbGNtZEFkVzVwYm1WMGRDNXViekNCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBdDhqTG9xSTFWVGx4QVoyYXhpRElUaFdjQU9YZHU4S2tWVVdhTi9Tb29POU8wUVE3S1JValNHS045Sks2NUFGUkRYUWtXUEF1NEhsbk80bm9ZbEZTTG5ZeUR4STY2TENyNzF4NGxnRkpqcUxlQXZCL0dxQnFGZklaM1lLL05yaG5VcUZ3WnU2M25MclpqY1VaeE5hUGpPT1NSU0RhWHB2MWtiNWszak9pU0dFQ0F3RUFBVEFOQmdrcWhraUc5dzBCQVFVRkFBT0JnUUJRWWo0Y0FhZldhWWZqQlUyemkxRWx3U3RJYUo1bnlwL3MvOEI4U0FQSzJUNzlNY015Y2NQM3dTVzEzTEhrbU0xandLZTNBQ0ZYQnZxR1FOMEliY0g0OWh1MEZLaFlGTS9HUERKY0lIRkJzaXlNQlhDaHB5ZTl2QmFUTkVCQ3RVM0tqanlHMGhSVDJtQVE5aCtia1BtT3ZsRW8vYUgweFI2OFo5aHc0UEYxM3c9PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0idXJuOmF1dGgwOmxvZ2luLWRldjMiIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50Ij5fOTVkYThhZjQ4MjY4NmEwY2VjZDY0Y2I3Y2FmOGU4NzFiN2FjMTFkYWUxPC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDEzLTA3LTA3VDEyOjAwOjE4WiIgUmVjaXBpZW50PSJodHRwczovL2xvZ2luLWRldjMuYXV0aDAuY29tOjMwMDAvbG9naW4vY2FsbGJhY2siIEluUmVzcG9uc2VUbz0iX2ZkMDY3N2ExZmRmMTU0Y2JmZGQwIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTMtMDctMDdUMTE6NTQ6NDhaIiBOb3RPbk9yQWZ0ZXI9IjIwMTMtMDctMDdUMTI6MDA6MThaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPnVybjphdXRoMDpsb2dpbi1kZXYzPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMy0wNy0wN1QxMToxMTo0MVoiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTMtMDctMDdUMTk6NTU6MThaIiBTZXNzaW9uSW5kZXg9Il81ZDA2MDZhMGIxZmQ5Nzk4ZDJhMjg3MjE5M2UzOWE5MDdhM2MwYmE0MTUiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1cmkiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPndvbG9za2k8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZ2l2ZW5OYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+TWF0aWFzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9InNuIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+V29sb3NraTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJjbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1cmkiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPk1hdGlhcyBXb2xvc2tpPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dXJpIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5tYXRpYXN3QGdtYWlsLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25QcmluY2lwYWxOYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+d29sb3NraUBybmQuZmVpZGUubm88L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZWR1UGVyc29uVGFyZ2V0ZWRJRCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1cmkiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPjFiMTI0NmQ3MjgxOTdiYjQ3ZDA5MzQyYWE0ZjZjM2Y0N2Y0ZTkyYWU8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idXJuOm9pZDowLjkuMjM0Mi4xOTIwMDMwMC4xMDAuMS4xIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+d29sb3NraTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1cm46b2lkOjIuNS40LjQyIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+TWF0aWFzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVybjpvaWQ6Mi41LjQuNCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1cmkiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPldvbG9za2k8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idXJuOm9pZDoyLjUuNC4zIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+TWF0aWFzIFdvbG9za2k8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idXJuOm9pZDowLjkuMjM0Mi4xOTIwMDMwMC4xMDAuMS4zIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+bWF0aWFzd0BnbWFpbC5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idXJuOm9pZDoxLjMuNi4xLjQuMS41OTIzLjEuMS4xLjYiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dXJpIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj53b2xvc2tpQHJuZC5mZWlkZS5ubzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1cm46b2lkOjEuMy42LjEuNC4xLjU5MjMuMS4xLjEuMTAiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dXJpIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj4xYjEyNDZkNzI4MTk3YmI0N2QwOTM0MmFhNGY2YzNmNDdmNGU5MmFlPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+' } }, function(err, response, body) { if(err) return done(err); @@ -53,7 +53,7 @@ describe('interop', function () { it('should validate response and not signature', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); it('should return a valid user', function(){ @@ -95,7 +95,7 @@ describe('interop', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback/samlp-okta', + uri: `${server.BASE_URL}/callback/samlp-okta`, form: { SAMLResponse: 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9hdXRoMDE0NS5hdXRoMC5jb20iIElEPSJpZDgxMzIzMDI4Njg0Njg5ODM4OTkzODY4MzEiIElzc3VlSW5zdGFudD0iMjAxMy0wOC0wM1QyMTo1NDo0My45NDJaIiBWZXJzaW9uPSIyLjAiPjxzYW1sMjpJc3N1ZXIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwOi8vd3d3Lm9rdGEuY29tL2s3eGtocTBqVUhVUFFBWFZNVUFOPC9zYW1sMjpJc3N1ZXI+PHNhbWwycDpTdGF0dXMgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1sMnA6U3RhdHVzPjxzYW1sMjpBc3NlcnRpb24geG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJpZDgxMzIzMDI4Njg1NDEwMTk3NTU0MTQxMjEiIElzc3VlSW5zdGFudD0iMjAxMy0wOC0wM1QyMTo1NDo0My45NDJaIiBWZXJzaW9uPSIyLjAiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI+PHNhbWwyOklzc3VlciBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPmh0dHA6Ly93d3cub2t0YS5jb20vazd4a2hxMGpVSFVQUUFYVk1VQU48L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz48ZHM6UmVmZXJlbmNlIFVSST0iI2lkODEzMjMwMjg2ODU0MTAxOTc1NTQxNDEyMSI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyI+PGVjOkluY2x1c2l2ZU5hbWVzcGFjZXMgeG1sbnM6ZWM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIgUHJlZml4TGlzdD0ieHMiLz48L2RzOlRyYW5zZm9ybT48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPjRHK3V2ZUttdGlCMUVrWTVCQXQrOGxtUXdqST08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+UTgwTjZGVXI1L1lQdEV6UmxSZE1vUHUrYkwwTXNzRHhOVVkreXh5a3pibXhzSTBqb0VvL1NtbVNnWnJEWVFLVGxsWmsvS2Z6Qk1QRlY5eUJINCttRXpDVTVFM3h1Q3M5OWpaemFmY3czSzhtSU1USnkxWUh4amMzNTlkMjdSNXM1MGk5dzVQSHN1c1JvdjBNalFJb0oydzQ4R3k0RW5ZYVZpcUJSM1VWRXFFPTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ25UQ0NBZ2FnQXdJQkFnSUdBVUJHSHhxVU1BMEdDU3FHU0liM0RRRUJCUVVBTUlHUk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFRwpBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFQ2d3RVQydDBZVEVVCk1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFakFRQmdOVkJBTU1DV3RzZFdkc1lXSnpNakVjTUJvR0NTcUdTSWIzRFFFSkFSWU4KYVc1bWIwQnZhM1JoTG1OdmJUQWVGdzB4TXpBNE1ETXlNVE00TXpoYUZ3MDBNekE0TURNeU1UTTVNemhhTUlHUk1Rc3dDUVlEVlFRRwpFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFCkNnd0VUMnQwWVRFVU1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFakFRQmdOVkJBTU1DV3RzZFdkc1lXSnpNakVjTUJvR0NTcUcKU0liM0RRRUpBUllOYVc1bWIwQnZhM1JoTG1OdmJUQ0JuekFOQmdrcWhraUc5dzBCQVFFRkFBT0JqUUF3Z1lrQ2dZRUFzQ0I5bEpUSApxQjd2ZE01amVPSDg0Y1c4dTdJSFl2NC9PQVBZRjBmQlllOXdKeTE5Q2d5TTJPZ2lBU3VBY0l0bkg0V2hCK2lvMlpQd2IvWHdsN1V1CjRYbVVFMGwrbWtDTnVEWXA1ZlhUWnh3djVHNkh2a0F4WFppbzBSazlUMFZFVENyb3hncFM1THhRL28vb3dqUjM5Uzd4elJuajZkZFgKM01xMnlHakt5QmNDQXdFQUFUQU5CZ2txaGtpRzl3MEJBUVVGQUFPQmdRQUIxcUdOcVNOTExXcStSUGNQK3dPYVd0WXBKT0o4L01iWgpFV1dtOS9LS0hLWE02Si96Z1VVSVhaaTNjek1lTytZK1gxNFBSOGxHWG9BSGY1Yi9KYXZHOUZtRnZSbjRmR2E0NVZUVm8yR2ZNTjZLCmFJS0Ywb2JlQ2JZaS9RVWY4QitYaTF0U0lKbTFWQ0tSRTdubmxpUS9UekdhTnVsZ1dleVRiVmtHMC9YOExRPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDI6U3ViamVjdCB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+PHNhbWwyOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+YWRtaW5Aa2x1Z2xhYnMuY29tPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDEzLTA4LTAzVDIxOjU5OjQzLjk0MloiIFJlY2lwaWVudD0iaHR0cHM6Ly9hdXRoMDE0NS5hdXRoMC5jb20iLz48L3NhbWwyOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sMjpTdWJqZWN0PjxzYW1sMjpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMy0wOC0wM1QyMTo0OTo0My45NDNaIiBOb3RPbk9yQWZ0ZXI9IjIwMTMtMDgtMDNUMjE6NTk6NDMuOTQyWiIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sMjpBdWRpZW5jZT5odHRwczovL2F1dGgwMTQ1LmF1dGgwLmNvbTwvc2FtbDI6QXVkaWVuY2U+PC9zYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDI6Q29uZGl0aW9ucz48c2FtbDI6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEzLTA4LTAzVDIxOjU0OjQzLjk0MloiIFNlc3Npb25JbmRleD0iaWQxMzc1NTY2ODgzOTQyLjY4NzYxMDQzNyIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdXRobkNvbnRleHQ+PHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWwyOkF1dGhuQ29udGV4dD48L3NhbWwyOkF1dGhuU3RhdGVtZW50PjxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iUm9sZSIgTmFtZUZvcm1hdD0ibnMiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPkFkbWluPC9zYW1sMjpBdHRyaWJ1dGVWYWx1ZT48L3NhbWwyOkF0dHJpYnV0ZT48L3NhbWwyOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWwyOkFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4=' } }, function(err, response, body) { if(err) return done(err); @@ -108,7 +108,7 @@ describe('interop', function () { it('should validate response and not signature', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); it('should return a valid user', function(){ @@ -124,7 +124,7 @@ describe('interop', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback/samlp-okta', + uri: `${server.BASE_URL}/callback/samlp-okta`, form: { SAMLResponse: 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9hdXRoMDE0NS5hdXRoMC5jb20iIElEPSJpZDgxMzIzMDI4Njg0Njg5ODM4OTkzODY4MzEiIElzc3VlSW5zdGFudD0iMjAxMy0wOC0wM1QyMTo1NDo0My45NDJaIiBWZXJzaW9uPSIyLjAiPjxzYW1sMjpJc3N1ZXIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwOi8vd3d3Lm9rdGEuY29tL2s3eGtocTBqVUhVUFFBWFZNVUFOPC9zYW1sMjpJc3N1ZXI+PHNhbWwycDpTdGF0dXMgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1sMnA6U3RhdHVzPjxzYW1sMjpBc3NlcnRpb24geG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJFVklMIiBJc3N1ZUluc3RhbnQ9IjIwMTMtMDgtMDNUMjE6NTQ6NDMuOTQyWiIgVmVyc2lvbj0iMi4wIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiPjxzYW1sMjpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vd3d3Lm9rdGEuY29tL2s3eGtocTBqVUhVUFFBWFZNVUFOPC9zYW1sMjpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+PGRzOlJlZmVyZW5jZSBVUkk9IiNpZDgxMzIzMDI4Njg1NDEwMTk3NTU0MTQxMjEiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiPjxlYzpJbmNsdXNpdmVOYW1lc3BhY2VzIHhtbG5zOmVjPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiIFByZWZpeExpc3Q9InhzIi8+PC9kczpUcmFuc2Zvcm0+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT40Ryt1dmVLbXRpQjFFa1k1QkF0KzhsbVF3akk9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PHNhbWwyOkFzc2VydGlvbiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9ImlkODEzMjMwMjg2ODU0MTAxOTc1NTQxNDEyMSIgSXNzdWVJbnN0YW50PSIyMDEzLTA4LTAzVDIxOjU0OjQzLjk0MloiIFZlcnNpb249IjIuMCIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIj48c2FtbDI6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5IiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cDovL3d3dy5va3RhLmNvbS9rN3hraHEwalVIVVBRQVhWTVVBTjwvc2FtbDI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjaWQ4MTMyMzAyODY4NTQxMDE5NzU1NDE0MTIxIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIj48ZWM6SW5jbHVzaXZlTmFtZXNwYWNlcyB4bWxuczplYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIiBQcmVmaXhMaXN0PSJ4cyIvPjwvZHM6VHJhbnNmb3JtPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+NEcrdXZlS210aUIxRWtZNUJBdCs4bG1Rd2pJPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5RODBONkZVcjUvWVB0RXpSbFJkTW9QdStiTDBNc3NEeE5VWSt5eHlremJteHNJMGpvRW8vU21tU2dackRZUUtUbGxaay9LZnpCTVBGVjl5Qkg0K21FekNVNUUzeHVDczk5alp6YWZjdzNLOG1JTVRKeTFZSHhqYzM1OWQyN1I1czUwaTl3NVBIc3VzUm92ME1qUUlvSjJ3NDhHeTRFbllhVmlxQlIzVVZFcUU9PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDblRDQ0FnYWdBd0lCQWdJR0FVQkdIeHFVTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUdSTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHDQpBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFQ2d3RVQydDBZVEVVDQpNQklHQTFVRUN3d0xVMU5QVUhKdmRtbGtaWEl4RWpBUUJnTlZCQU1NQ1d0c2RXZHNZV0p6TWpFY01Cb0dDU3FHU0liM0RRRUpBUllODQphVzVtYjBCdmEzUmhMbU52YlRBZUZ3MHhNekE0TURNeU1UTTRNemhhRncwME16QTRNRE15TVRNNU16aGFNSUdSTVFzd0NRWURWUVFHDQpFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFDQpDZ3dFVDJ0MFlURVVNQklHQTFVRUN3d0xVMU5QVUhKdmRtbGtaWEl4RWpBUUJnTlZCQU1NQ1d0c2RXZHNZV0p6TWpFY01Cb0dDU3FHDQpTSWIzRFFFSkFSWU5hVzVtYjBCdmEzUmhMbU52YlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXNDQjlsSlRIDQpxQjd2ZE01amVPSDg0Y1c4dTdJSFl2NC9PQVBZRjBmQlllOXdKeTE5Q2d5TTJPZ2lBU3VBY0l0bkg0V2hCK2lvMlpQd2IvWHdsN1V1DQo0WG1VRTBsK21rQ051RFlwNWZYVFp4d3Y1RzZIdmtBeFhaaW8wUms5VDBWRVRDcm94Z3BTNUx4US9vL293alIzOVM3eHpSbmo2ZGRYDQozTXEyeUdqS3lCY0NBd0VBQVRBTkJna3Foa2lHOXcwQkFRVUZBQU9CZ1FBQjFxR05xU05MTFdxK1JQY1Ard09hV3RZcEpPSjgvTWJaDQpFV1dtOS9LS0hLWE02Si96Z1VVSVhaaTNjek1lTytZK1gxNFBSOGxHWG9BSGY1Yi9KYXZHOUZtRnZSbjRmR2E0NVZUVm8yR2ZNTjZLDQphSUtGMG9iZUNiWWkvUVVmOEIrWGkxdFNJSm0xVkNLUkU3bm5saVEvVHpHYU51bGdXZXlUYlZrRzAvWDhMUT09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWwyOlN1YmplY3QgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPmFkbWluQGtsdWdsYWJzLmNvbTwvc2FtbDI6TmFtZUlEPjxzYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxMy0wOC0wM1QyMTo1OTo0My45NDJaIiBSZWNpcGllbnQ9Imh0dHBzOi8vYXV0aDAxNDUuYXV0aDAuY29tIi8+PC9zYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDI6U3ViamVjdD48c2FtbDI6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTMtMDgtMDNUMjE6NDk6NDMuOTQzWiIgTm90T25PckFmdGVyPSIyMDEzLTA4LTAzVDIxOjU5OjQzLjk0MloiIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDI6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDI6QXVkaWVuY2U+aHR0cHM6Ly9hdXRoMDE0NS5hdXRoMC5jb208L3NhbWwyOkF1ZGllbmNlPjwvc2FtbDI6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWwyOkNvbmRpdGlvbnM+PHNhbWwyOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMy0wOC0wM1QyMTo1NDo0My45NDJaIiBTZXNzaW9uSW5kZXg9ImlkMTM3NTU2Njg4Mzk0Mi42ODc2MTA0MzciIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDI6QXV0aG5Db250ZXh0PjxzYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvc2FtbDI6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sMjpBdXRobkNvbnRleHQ+PC9zYW1sMjpBdXRoblN0YXRlbWVudD48c2FtbDI6QXR0cmlidXRlU3RhdGVtZW50IHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDI6QXR0cmlidXRlIE5hbWU9IlJvbGUiIE5hbWVGb3JtYXQ9Im5zIj48c2FtbDI6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5BZG1pbjwvc2FtbDI6QXR0cmlidXRlVmFsdWU+PC9zYW1sMjpBdHRyaWJ1dGU+PC9zYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sMjpBc3NlcnRpb24+PGRzOlNpZ25hdHVyZVZhbHVlPlE4ME42RlVyNS9ZUHRFelJsUmRNb1B1K2JMME1zc0R4TlVZK3l4eWt6Ym14c0kwam9Fby9TbW1TZ1pyRFlRS1RsbFprL0tmekJNUEZWOXlCSDQrbUV6Q1U1RTN4dUNzOTlqWnphZmN3M0s4bUlNVEp5MVlIeGpjMzU5ZDI3UjVzNTBpOXc1UEhzdXNSb3YwTWpRSW9KMnc0OEd5NEVuWWFWaXFCUjNVVkVxRT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNuVENDQWdhZ0F3SUJBZ0lHQVVCR0h4cVVNQTBHQ1NxR1NJYjNEUUVCQlFVQU1JR1JNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUcNCkExVUVDQXdLUTJGc2FXWnZjbTVwWVRFV01CUUdBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVOTUFzR0ExVUVDZ3dFVDJ0MFlURVUNCk1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFakFRQmdOVkJBTU1DV3RzZFdkc1lXSnpNakVjTUJvR0NTcUdTSWIzRFFFSkFSWU4NCmFXNW1iMEJ2YTNSaExtTnZiVEFlRncweE16QTRNRE15TVRNNE16aGFGdzAwTXpBNE1ETXlNVE01TXpoYU1JR1JNUXN3Q1FZRFZRUUcNCkV3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFV01CUUdBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVOTUFzR0ExVUUNCkNnd0VUMnQwWVRFVU1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFakFRQmdOVkJBTU1DV3RzZFdkc1lXSnpNakVjTUJvR0NTcUcNClNJYjNEUUVKQVJZTmFXNW1iMEJ2YTNSaExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBc0NCOWxKVEgNCnFCN3ZkTTVqZU9IODRjVzh1N0lIWXY0L09BUFlGMGZCWWU5d0p5MTlDZ3lNMk9naUFTdUFjSXRuSDRXaEIraW8yWlB3Yi9Yd2w3VXUNCjRYbVVFMGwrbWtDTnVEWXA1ZlhUWnh3djVHNkh2a0F4WFppbzBSazlUMFZFVENyb3hncFM1THhRL28vb3dqUjM5Uzd4elJuajZkZFgNCjNNcTJ5R2pLeUJjQ0F3RUFBVEFOQmdrcWhraUc5dzBCQVFVRkFBT0JnUUFCMXFHTnFTTkxMV3ErUlBjUCt3T2FXdFlwSk9KOC9NYloNCkVXV205L0tLSEtYTTZKL3pnVVVJWFppM2N6TWVPK1krWDE0UFI4bEdYb0FIZjViL0phdkc5Rm1GdlJuNGZHYTQ1VlRWbzJHZk1ONksNCmFJS0Ywb2JlQ2JZaS9RVWY4QitYaTF0U0lKbTFWQ0tSRTdubmxpUS9UekdhTnVsZ1dleVRiVmtHMC9YOExRPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDI6U3ViamVjdCB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+PHNhbWwyOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+YWRtaW5Aa2x1Z2xhYnMuY29tPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDEzLTA4LTAzVDIxOjU5OjQzLjk0MloiIFJlY2lwaWVudD0iaHR0cHM6Ly9hdXRoMDE0NS5hdXRoMC5jb20iLz48L3NhbWwyOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sMjpTdWJqZWN0PjxzYW1sMjpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMy0wOC0wM1QyMTo0OTo0My45NDNaIiBOb3RPbk9yQWZ0ZXI9IjIwMTMtMDgtMDNUMjE6NTk6NDMuOTQyWiIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sMjpBdWRpZW5jZT5odHRwczovL2F1dGgwMTQ1LmF1dGgwLmNvbTwvc2FtbDI6QXVkaWVuY2U+PC9zYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDI6Q29uZGl0aW9ucz48c2FtbDI6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEzLTA4LTAzVDIxOjU0OjQzLjk0MloiIFNlc3Npb25JbmRleD0iaWQxMzc1NTY2ODgzOTQyLjY4NzYxMDQzNyIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdXRobkNvbnRleHQ+PHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWwyOkF1dGhuQ29udGV4dD48L3NhbWwyOkF1dGhuU3RhdGVtZW50PjxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iUm9sZSIgTmFtZUZvcm1hdD0ibnMiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPkVWSUw8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjwvc2FtbDI6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg==' } }, function(err, response) { if(err) return done(err); @@ -135,7 +135,7 @@ describe('interop', function () { it('should return error', function(){ expect(r.statusCode) - .to.equal(400); + .to.equal(400); }); }); @@ -146,7 +146,7 @@ describe('interop', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback', + uri: `${server.BASE_URL}/callback`, form: { SAMLResponse: SAMLResponse } }, function(err, response) { if(err) return done(err); @@ -156,9 +156,8 @@ describe('interop', function () { }); it('should return error', function(){ - console.log({ body: r.body }); expect(r.statusCode) - .to.equal(400); + .to.equal(400); }); }); @@ -169,7 +168,7 @@ describe('interop', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback/samlp-ping', + uri: `${server.BASE_URL}/callback/samlp-ping`, form: { SAMLResponse: 'PHNhbWxwOlJlc3BvbnNlIERlc3RpbmF0aW9uPSJodHRwczovL2xvZ2luLWRldjMuYXV0aDAuY29tOjMwMDAvbG9naW4vY2FsbGJhY2siIEluUmVzcG9uc2VUbz0iXzRhNDMyMzEzNmNhMGFkNDU3OGNiIiBJc3N1ZUluc3RhbnQ9IjIwMTMtMDctMDhUMTk6NDA6MjUuNTIxWiIgSUQ9IklEMzBlNjM2OTcyYzgxYmNkN2E1MzBkMWJkNjc4MmM4YmNiZTU2Y2U4NjEwZDM0ZDJjMDIiIFZlcnNpb249IjIuMCIgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCI+PHNhbWw6SXNzdWVyIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPlBpbmdDb25uZWN0PC9zYW1sOklzc3Vlcj48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpBc3NlcnRpb24gVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTMtMDctMDhUMTk6NDA6MjUuNTIxWiIgSUQ9IklENzc1MzVlNjc2YjM0ZDQyNzAzMWM3NTM5ODk2Nzg5YzE5NTM4ZTdlNmRmNTY5YWQ4MDIiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sOklzc3Vlcj5QaW5nQ29ubmVjdDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CjxkczpTaWduZWRJbmZvPgo8ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgo8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+CjxkczpSZWZlcmVuY2UgVVJJPSIjSUQ3NzUzNWU2NzZiMzRkNDI3MDMxYzc1Mzk4OTY3ODljMTk1MzhlN2U2ZGY1NjlhZDgwMiI+CjxkczpUcmFuc2Zvcm1zPgo8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgo8L2RzOlRyYW5zZm9ybXM+CjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPgo8ZHM6RGlnZXN0VmFsdWU+elRZdENWTVpBUDJYZGFJRDM3VlMyZ1MrTEwwPTwvZHM6RGlnZXN0VmFsdWU+CjwvZHM6UmVmZXJlbmNlPgo8L2RzOlNpZ25lZEluZm8+CjxkczpTaWduYXR1cmVWYWx1ZT4KUUNaRGtMMXRzZzFISDNtQVd3UDJzYWlRVFNldTBsTTdmdXNCQmlsL3RDdGpkU2xlZXgra1JETFpvY2p1SytVcDEvN0poS0NOVVE2SApOcVd0cWZHVVJpcUpaKzI5UVoxdzZibU93YXVzZzFiUkJGb0VtUkd0cER4dTBwNHNXemlOdzJ0eU81b1ZWSDYwaEFmZllHTjcweUg0CkUxd2t1emVhRVhOWFhGaEFKeHNOVGo1SkdEZmNld1U5UjQvVW1yTlVYWTdrdkJRV3BRcmI1VE5TNjNrL0hhaEdsN3pPNzJla1FVK04KU0FUU0Eyb2hJU09obVBTbUVyQVVaQkI2Z0FSQ2srT2xWRld0VS9NUnFFMlo5UWFXNndkYlBnVkg0d2JYbnA4YkcxdTQ0VHk5bUFDWApiKzhXT0F3NHBYTkxXYzVZK2dKaVFyaUdyb3p6b0pQaDVGL3FDZz09CjwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPgo8ZHM6WDUwOURhdGE+CjxkczpYNTA5Q2VydGlmaWNhdGU+Ck1JSUZ4akNDQks2Z0F3SUJBZ0lRYXFiblNYTklDQWRmVFo4Sm9pcU9VakFOQmdrcWhraUc5dzBCQVFVRkFEQmlNUXN3Q1FZRFZRUUcKRXdKVlV6RWhNQjhHQTFVRUNoTVlUbVYwZDI5eWF5QlRiMngxZEdsdmJuTWdUQzVNTGtNdU1UQXdMZ1lEVlFRREV5ZE9aWFIzYjNKcgpJRk52YkhWMGFXOXVjeUJEWlhKMGFXWnBZMkYwWlNCQmRYUm9iM0pwZEhrd0hoY05NVEl3TXpBeU1EQXdNREF3V2hjTk1UUXdPREk0Ck1qTTFPVFU1V2pDQnp6RUxNQWtHQTFVRUJoTUNWVk14RGpBTUJnTlZCQkVUQlRnd01qQXlNUXN3Q1FZRFZRUUlFd0pEVHpFUE1BMEcKQTFVRUJ4TUdSR1Z1ZG1WeU1SSXdFQVlEVlFRSkV3bFRkV2wwWlNBeE1EQXhHVEFYQmdOVkJBa1RFREV3TURFZ01UZDBhQ0JUZEhKbApaWFF4SWpBZ0JnTlZCQW9UR1ZCcGJtY2dTV1JsYm5ScGRIa2dRMjl5Y0c5eVlYUnBiMjR4R0RBV0JnTlZCQXNURDFObFkzVnlaU0JNCmFXNXJJRk5UVERFbE1DTUdBMVVFQXhNY2MzTnZMbU52Ym01bFkzUXVjR2x1WjJsa1pXNTBhWFI1TG1OdmJUQ0NBU0l3RFFZSktvWkkKaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKZjF1UnZkSTJIdHJtNFNLUTZJd0xtSk9MeXJZdDNvdlJHaUJjejVoLzdmM3BqQwp4UjNYaml1dlVzZjR3T0pVWDAwRzNhcXpvK1o3VGVIRkJPOXA1UnBPWlFwK01tRDFGWko4ZlNUQVZjTDhUZHZKRjFKWUNaZEt3bCtMCk1MS0ZtWnZRMFljR0NOcEVmL1NRRlI1VlJUZzNmekV3eWdMMklsRFpYbXJ6RklBTk04R0JGUFNrZ3JoZitad3lsNnY1TVo2VEhQemUKSlpzMkZnek05MjVkZUNhZDUwMWZJOWtsdnRyRVN6NCtrZUxITEN3TFU2dCtKYW5vMzJneHZLYzNLZ2VyWlJkZ2pEcGI1MVpjRE10WQo4dTZBMXVVUGpnSmdPTHkvVFd5NUpXaE4vQmlwQkNGWEFXRTZCRDdKb3lmUEJTTjlRaE51cWR2WHM3NVhyRmxmNUk4Q0F3RUFBYU9DCkFnZ3dnZ0lFTUI4R0ExVWRJd1FZTUJhQUZEeEI0bzhJQ0tsTUpZbU5iY1U0MFB5RmpHSVhNQjBHQTFVZERnUVdCQlRRdHNIWU0wU0UKK0JwUGdUcUFudDhtb05CeHp6QU9CZ05WSFE4QkFmOEVCQU1DQmFBd0RBWURWUjBUQVFIL0JBSXdBREFkQmdOVkhTVUVGakFVQmdncgpCZ0VGQlFjREFRWUlLd1lCQlFVSEF3SXdhd1lEVlIwZ0JHUXdZakJnQmd3ckJnRUVBWVlPQVFJQkF3RXdVREJPQmdnckJnRUZCUWNDCkFSWkNhSFIwY0RvdkwzZDNkeTV1WlhSM2IzSnJjMjlzZFhScGIyNXpMbU52YlM5c1pXZGhiQzlUVTB3dGJHVm5ZV3d0Y21Wd2IzTnAKZEc5eWVTMWpjSE11YW5Od01Ib0dBMVVkSHdSek1IRXdOcUEwb0RLR01HaDBkSEE2THk5amNtd3VibVYwYzI5c2MzTnNMbU52YlM5TwpaWFIzYjNKclUyOXNkWFJwYjI1elgwTkJMbU55YkRBM29EV2dNNFl4YUhSMGNEb3ZMMk55YkRJdWJtVjBjMjlzYzNOc0xtTnZiUzlPClpYUjNiM0pyVTI5c2RYUnBiMjV6WDBOQkxtTnliREJ6QmdnckJnRUZCUWNCQVFSbk1HVXdQQVlJS3dZQkJRVUhNQUtHTUdoMGRIQTYKTHk5M2QzY3VibVYwYzI5c2MzTnNMbU52YlM5T1pYUjNiM0pyVTI5c2RYUnBiMjV6WDBOQkxtTnlkREFsQmdnckJnRUZCUWN3QVlZWgphSFIwY0RvdkwyOWpjM0F1Ym1WMGMyOXNjM05zTG1OdmJUQW5CZ05WSFJFRUlEQWVnaHh6YzI4dVkyOXVibVZqZEM1d2FXNW5hV1JsCmJuUnBkSGt1WTI5dE1BMEdDU3FHU0liM0RRRUJCUVVBQTRJQkFRQ0h1elNEaHg0Ukd5N1JiQ0FJMG1maWR2bXR6ZmFMVm9LaUIzVFoKSlVmNmpreXF5NmN3bjA5bjQxdW1kdUpZRHU4K2p2ODFKZUxXdGpabWxYZmhzb3ZhQlRJRVd4aUkxTlFLNlY5Rjk3ZDM3bHF5c3ZJbQprSUliaFVKaEJvSW5ZUldLM1dxYXd6V295Vjh2eFAxaDdNam81c1BOZ3FmTnNVVm5iOVBJODA0bFg3UEZZSHJxS0tudXJxb09kTHJvCnpGU2pBeUU0NElFNWdHWWVqN05SbE5EY1VRcEZ3Yjd6MkJqTVJtQ3RReTA2VlRjSFVlZDdNcWc5WHNNVHA4WXlDbW9uWkFlTGpXYTkKZnFDcVFJZzZvT3BTNzhKeXN4bGVFM3YvUWtYd0tmdGwzZGFiT2dwL1hDMzF6VW1MRDhqTzZLYmRpTzhrQ1B1TFFIUEl2c0ZvYXRDSQo8L2RzOlg1MDlDZXJ0aWZpY2F0ZT4KPC9kczpYNTA5RGF0YT4KPGRzOktleVZhbHVlPgo8ZHM6UlNBS2V5VmFsdWU+CjxkczpNb2R1bHVzPgpsL1c1RzkwalllMnViaElwRG9qQXVZazR2S3RpM2VpOUVhSUZ6UG1IL3QvZW1NTEZIZGVPSzY5U3gvakE0bFJmVFFiZHFyT2o1bnROCjRjVUU3Mm5sR2s1bENuNHlZUFVWa254OUpNQlZ3dnhOMjhrWFVsZ0psMHJDWDRzd3NvV1ptOURSaHdZSTJrUi85SkFWSGxWRk9EZC8KTVRES0F2WWlVTmxlYXZNVWdBMHp3WUVVOUtTQ3VGLzVuREtYcS9reG5wTWMvTjRsbXpZV0RNejNibDE0SnAzblRWOGoyU1crMnNSTApQajZSNHNjc0xBdFRxMzRscWVqZmFERzhwemNxQjZ0bEYyQ01PbHZuVmx3TXkxank3b0RXNVErT0FtQTR2TDlOYkxrbGFFMzhHS2tFCklWY0JZVG9FUHNtako4OEZJMzFDRTI2cDI5ZXp2bGVzV1Yva2p3PT0KPC9kczpNb2R1bHVzPgo8ZHM6RXhwb25lbnQ+QVFBQjwvZHM6RXhwb25lbnQ+CjwvZHM6UlNBS2V5VmFsdWU+CjwvZHM6S2V5VmFsdWU+CjwvZHM6S2V5SW5mbz4KPC9kczpTaWduYXR1cmU+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnRlc3R1c2VyMUB0ZXN0aWRwLmNvbm5lY3QucGluZ2lkZW50aXR5LmNvbTwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iXzRhNDMyMzEzNmNhMGFkNDU3OGNiIiBOb3RPbk9yQWZ0ZXI9IjIwMTMtMDctMDhUMjA6MTA6MjUuNTIxWiIgUmVjaXBpZW50PSJodHRwczovL2xvZ2luLWRldjMuYXV0aDAuY29tOjMwMDAvbG9naW4vY2FsbGJhY2siLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdE9uT3JBZnRlcj0iMjAxMy0wNy0wOFQyMDoxMDoyNS41MjFaIiBOb3RCZWZvcmU9IjIwMTMtMDctMDhUMTk6MzA6MjUuNTIxWiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT51cm46YXV0aDA6bG9naW4tZGV2Mzwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTMtMDctMDhUMTk6NDA6MjUuNTIyWiIgU2Vzc2lvbkluZGV4PSJsTGJTYlhpZmFPREx3T2hCZ1AyZk5PdVc5RmoiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48c2FtbDpBdXRoZW50aWNhdGluZ0F1dGhvcml0eT50ZXN0aWRwLmNvbm5lY3QucGluZ2lkZW50aXR5LmNvbTwvc2FtbDpBdXRoZW50aWNhdGluZ0F1dGhvcml0eT48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIj48c2FtbDpBdHRyaWJ1dGUgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyIgTmFtZT0iUGluZ09uZS5pZHBpZCI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+YjE0ZWIwYjYtYTMzYS00MTRkLTljNzctMDEzMWUzMjRhNWI3PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiIE5hbWU9Im5hbWVpZCI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+dGVzdF9uYW1laWQ8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyIgTmFtZT0iUGluZ09uZS5BdXRoZW50aWNhdGluZ0F1dGhvcml0eSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+dGVzdGlkcC5jb25uZWN0LnBpbmdpZGVudGl0eS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4=' } }, function(err, response, body) { if(err) return done(err); @@ -182,7 +181,7 @@ describe('interop', function () { it('should validate response and not signature', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); it('should return a valid user', function(){ @@ -195,10 +194,10 @@ describe('interop', function () { var signedAssertion = '<saml:Assertion MajorVersion="1" MinorVersion="1" AssertionID="_8c8a1b2e-7ed4-4b32-82ce-83c6d72bb297" Issuer="https://test-adfs.auth0.com" IssueInstant="2013-07-11T12:32:02.990Z" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"><saml:Conditions NotBefore="2013-07-11T12:32:02.985Z" NotOnOrAfter="2013-07-11T13:32:02.985Z"><saml:AudienceRestrictionCondition><saml:Audience>urn:auth0:auth0</saml:Audience></saml:AudienceRestrictionCondition></saml:Conditions><saml:AttributeStatement><saml:Subject><saml:NameIdentifier>john@fabrikam.com</saml:NameIdentifier><saml:SubjectConfirmation><saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod></saml:SubjectConfirmation></saml:Subject><saml:Attribute AttributeName="emailaddress" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims"><saml:AttributeValue>john@fabrikam.com</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeName="name" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims"><saml:AttributeValue>John Fabrikam</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeName="givenname" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims"><saml:AttributeValue>John</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeName="surname" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims"><saml:AttributeValue>Fabrikam</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthenticationStatement AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password" AuthenticationInstant="2013-07-11T12:32:02.881Z"><saml:Subject><saml:NameIdentifier>john@fabrikam.com</saml:NameIdentifier><saml:SubjectConfirmation><saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod></saml:SubjectConfirmation></saml:Subject></saml:AuthenticationStatement><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></ds:SignatureMethod><ds:Reference URI="#_8c8a1b2e-7ed4-4b32-82ce-83c6d72bb297"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod><ds:DigestValue>8Yi8+vbZagbCsopdmXFjeFvexHlkHYAViSf9w3yFbWo=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>SzQaNU4uo4IPJTsK3DZkYNx1FzpAoIxXyWiOMJyuUScYbvUHMjxoPsh6KJrkLwUIGnv07TpTFKrhjA/tGzLCwPMOWlpGp66N/U9wWe3vshHyW34/oAq14EwptjmXEPpHjR2P+giJAUJ0VTXIvbTsGNLuDVV/px43CoIX2D6jc8yt8VffAMX4R+WzI2a6QRMqglTbxzFEfcJC1yK9jT/UzjRIKe9bMS2T8kupDaGOWYj4XMh9BkIVXV40jJakss+cF4wjO/LWfpbwZwMzfOXBQV+W6O5X/HfTod/4zzmdFKjArx6vXQl7vCRr9AXUGgbPWdtSVK7HSMCyjZiAOGcr0Q==</ds:SignatureValue><KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><X509Data><X509Certificate>MIIC5DCCAcygAwIBAgIQGmfE0ae34q1IXJvpfhwYVDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNBREZTIFNpZ25pbmcgLSB0ZXN0LWFkZnMuYXV0aDEwLmNvbTAeFw0xMzAyMTIwMzU3MzlaFw0xNDAyMTIwMzU3MzlaMC4xLDAqBgNVBAMTI0FERlMgU2lnbmluZyAtIHRlc3QtYWRmcy5hdXRoMTAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuNaTwlMjKeXVabhVQJjmjousrLuYi5hxELIQX80ZT3/kTsRUQaP6AoCMEYjofcV6QnxQeHxHJgzcy5t4bmHozIsLYlroH7PaDNqXLnX+6ivAX1nFa9IOeaw3X206oGmCSo6pJxbW8LwXjYC7BYrINitm/mgcOrypbasADvO1j+fyjkBTFPuF2+La4ehwGCKdrvZZF0lDKiHPbCI8xqH7HOgX+QLtnhK2WclDIpcPd2eeaVq1fB8XBwTXNOv2ZdaEi2i3mDXQoa8wZVozqs5h6OKVXl7hNQH2/qUDAZc15kZcR9Zcxyo5v0GtqYS3J70Q+8HkuQfYTBGR+Cum9S+bfQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAVf930SJfJquM/4A3vibsmjRuCytHeEXLrEyt0j/NBDnyLqFMoL7qILfqtK3oSmQNb6PRQiLLFJlIz7fnkj6k3hm4qpJJvjyqcxYDeW6SK8yP7zKNGMeyIMs/humVgXWALkTF6PoFIfFo2lfnHAzHqPE06WN+hcYHRBjuR5/T+58l2LH9vJjPHuceGkWXyQCB/Hd2riNElXODIBEKzzKL923rm3oPaH/Un8TYnpyRRVKLd40DiptbY/E7YpzWrOsUppjsm0pUhdvBYjihLc6PhALzoUC3GwMjUK0T70gqRoGzsiqRIjIEC35QBGKavzhya2DLTZK3Pf70eJpkp1bGL</X509Certificate></X509Data></KeyInfo></ds:Signature></saml:Assertion>'; var saml_passport = new SamlPassport({thumbprints: ['C9018666E764613366C20BC011D947B39BED236B'], - realm: 'urn:auth0:auth0', - checkExpiration: false, - checkRecipient: false}); // dont check expiration since we are harcoding the token - var profile = saml_passport.validateSamlAssertion(signedAssertion, function(err, profile) { + realm: 'urn:auth0:auth0', + checkExpiration: false, + checkRecipient: false}); // dont check expiration since we are harcoding the token + var profile = saml_passport.validateSamlAssertion(signedAssertion, {}, function(err, profile) { if (err) return done(err); assert.ok(profile); done(); @@ -210,10 +209,10 @@ describe('interop', function () { var signedAssertion = '<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="_071de65ecb79185206fcb0789e9afd90" IssueInstant="2014-04-06T22:27:04.997Z" Version="2.0"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://aai-logon.ethz.ch/idp/shibboleth</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod><ds:Reference URI="#_071de65ecb79185206fcb0789e9afd90"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"></ec:InclusiveNamespaces></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>jVMwKZ5O3hXfOf6tkVan2hnPW2w=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>nq5nJangoli5J6uBF/sEeYyKL7+xepbsDmjT6mpggLmba6yR+lQaZmAGnti8nhZUPyXwZfZS3d9oH4upbRg56jdVVcPaZUhYOPW2T2etm7lxxaDlHDJo/E40KnBtGMn6Oxz23hXUrc6p6K4FFLCQwmsE3ZZlP/u8DcqKNl5X/D5udcCV75mjxnVKWuXu34Xw4uQEQBb+6UfGjDN1/91M6U3ZZ0iOSRsBC7+SYLVMbDZqGveioKjZMPBuHmoBwQxsCixu1var3LNyCFVRo0LV9qA5DhA5lyH209+kFsN9vqzHKkiOF+Wua+Ngh2oR/48CWfTOjDuvRpje1bICIwwCQg==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIFjzCCBHegAwIBAgIUZ+QtvaEucMtOcruHlzQrEDH92FMwDQYJKoZIhvcNAQEFBQAwazELMAkG\nA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHzAdBgNVBAsTFnd3dy5xdW92YWRp\nc2dsb2JhbC5jb20xIDAeBgNVBAMTF1F1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBMB4XDTEzMDQxNzA4\nMDYwNFoXDTE1MDQxNzA4MDYwNFowYzELMAkGA1UEBhMCQ0gxEDAOBgNVBAgTB1p1ZXJpY2gxEDAO\nBgNVBAcTB1p1ZXJpY2gxFDASBgNVBAoTC0VUSCBadWVyaWNoMRowGAYDVQQDExFhYWktbG9nb24u\nZXRoei5jaDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOJWLI4vWx5HnqUvkBDm5Egp\nUg8yOlL3HbS0Y62/k77R2W9wxNczcR79wUBl2cNDCF/LxzdY1ml2u2skbZy4tqtmcvHVrwM5RVDb\n3jpjUhzBlD5rkpxgut2zFmNsahXzceD9dzsTvq7MUq6YgW6iRY3wNbes7ZgRtdkCz+vbiB52iTES\nZ2lo6fBn69eiqywUhQ5t/K4jGqpSUf1DITz//lMWRveagVyUq342JONxo93nt6x6ewGg+Qo8yCuC\nj4VehpncHYV0oNI2sSncKPm23Z4TNxPDalSaq8R5nKhueG+FHX7Ks8hWYSf42m2rrZLTumv2Ry8H\nFrPFkI7kuSFwVRECAwEAAaOCAjEwggItMHQGCCsGAQUFBwEBBGgwZjAqBggrBgEFBQcwAYYeaHR0\ncDovL29jc3AucXVvdmFkaXNnbG9iYWwuY29tMDgGCCsGAQUFBzAChixodHRwOi8vdHJ1c3QucXVv\ndmFkaXNnbG9iYWwuY29tL3F2c3NsaWNhLmNydDCBtQYDVR0RBIGtMIGqghFhYWktbG9nb24uZXRo\nei5jaIIPdmNpcGhlci5ldGh6LmNogg92Y2Flc2FyLmV0aHouY2iCD3ZjdXJ0ZXIuZXRoei5jaIIP\ndmNvcHBlci5ldGh6LmNogg92Y2Vuc29yLmV0aHouY2iCEmxkYXBzLWluZm8uZXRoei5jaIIPbGlu\ndGVzdC5ldGh6LmNogRt2bGFkaXNsYXYubmVzcG9yQGlkLmV0aHouY2gwUQYDVR0gBEowSDBGBgwr\nBgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2dsb2JhbC5jb20v\ncmVwb3NpdG9yeTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC\nMB8GA1UdIwQYMBaAFDJNoU/q8K6Ztu6bByyECBFQi+J+MDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6\nLy9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2c3NsaWNhLmNybDAdBgNVHQ4EFgQUUrfY5AJdnN5W\n9TTyrVObbQEoH/cwDQYJKoZIhvcNAQEFBQADggEBAJHQIjLbalw9LF9wIjhhOsEsaf/Bd8dSKcb2\nICLC16TyetuTTJfqHqHr3QiAcrSNKOxqoFBX51t7oNyd3n1BGxJeYmpoyKHKmViUF9mJWBKxSvfW\njmYA7M/LptNX+aUz0fPntCokjH5pPAk3n5YYf2gTFOmRbZDdvNxQ0+o5EkRKkxLDAYM7HlJshWfK\nyY8ZKiPSx28ebXORGzW/VC5VunURFPmhvy5hUFo2qFhGhkQZD1Tg5uN+vd7KywgXLiQKWFDweOxY\nkFuTatM9peWNaapAuaYL8D6q/pn6q76cDKiMjTLp1siQsVVzFAZNjywOve5tdqB/Qo7zwX7TggF1\nmrQ=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Subject><saml2:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient" NameQualifier="https://aai-logon.ethz.ch/idp/shibboleth">_e132eb870c4a912c56e1bafeb5257b35</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData Address="80.218.183.64" InResponseTo="_e8cda4c13682e111e66d" NotOnOrAfter="2014-04-06T22:32:04.997Z" Recipient="https://fmi-test.auth0.com/login/callback"></saml2:SubjectConfirmationData></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2014-04-06T22:27:04.997Z" NotOnOrAfter="2014-04-06T22:32:04.997Z"><saml2:AudienceRestriction><saml2:Audience>urn:auth0:fmi-test</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2014-04-06T22:27:04.858Z" SessionIndex="_ff0e0b5d9d6706bc22561c49c7eac971"><saml2:SubjectLocality Address="80.218.183.64"></saml2:SubjectLocality><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute FriendlyName="eduPersonAffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">member</saml2:AttributeValue><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">staff</saml2:AttributeValue><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">student</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="sn" Name="urn:oid:2.5.4.4" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Gnügge</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="givenName" Name="urn:oid:2.5.4.42" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Robert</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="swissEduPersonHomeOrganization" Name="urn:oid:2.16.756.1.2.5.1.1.4" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">ethz.ch</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="swissEduPersonUniqueID" Name="urn:oid:2.16.756.1.2.5.1.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">187624@ethz.ch</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="swissEduPersonHomeOrganizationType" Name="urn:oid:2.16.756.1.2.5.1.1.5" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">university</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="eduPersonTargetedID" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue><saml2:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" NameQualifier="https://aai-logon.ethz.ch/idp/shibboleth" SPNameQualifier="urn:auth0:fmi-test">37J7PjSu8hkThPDMZOfZLtca0Ag=</saml2:NameID></saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="mail" Name="urn:oid:0.9.2342.19200300.100.1.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">robert.gnuegge@bsse.ethz.ch</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion>'; var saml_passport = new SamlPassport({thumbprints: ['42FA24A83E107F6842E05D2A2CA0A0A0CA8A2031'], - realm: 'urn:auth0:fmi-test', - recipientUrl: 'https://fmi-test.auth0.com/login/callback', - checkExpiration: false}); // dont check expiration since we are harcoding the token - var profile = saml_passport.validateSamlAssertion(signedAssertion, function(err, profile) { + realm: 'urn:auth0:fmi-test', + recipientUrl: 'https://fmi-test.auth0.com/login/callback', + checkExpiration: false}); // dont check expiration since we are harcoding the token + var profile = saml_passport.validateSamlAssertion(signedAssertion, {}, function(err, profile) { if (err) return done(err); assert.ok(profile); done(); @@ -240,7 +239,7 @@ describe('interop', function () { var saml_passport = new SamlPassport(samlOptions); var sp = new samlp(samlpOptions, saml_passport); - sp.validateSamlResponse(signedResponse, function(err, profile) { + sp.validateSamlResponse(signedResponse, {}, function(err, profile) { if (err) return done(err); assert.ok(profile); expect(profile).to.have.property('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier', 'kchen七味@shichimitogarashi.org'); @@ -265,13 +264,13 @@ describe('interop', function () { var sm = new SamlPassport(samlOptions); var sp = new samlp(samlpOptions, sm); - sp.validateSamlResponse(new Buffer(response, 'base64').toString(), - function(err, profile){ - if (err) return done(err); - assert.ok(profile); - expect(profile['fname']).to.equal('Pushp'); - done(); - }); + sp.validateSamlResponse(new Buffer(response, 'base64').toString(), {}, + function(err, profile){ + if (err) return done(err); + assert.ok(profile); + expect(profile['fname']).to.equal('Pushp'); + done(); + }); }); @@ -291,14 +290,14 @@ describe('interop', function () { var sm = new SamlPassport(samlOptions); var sp = new samlp(samlpOptions, sm); - sp.validateSamlResponse(new Buffer(response, 'base64').toString(), - function(err, profile){ - if (err) { - assert.ok(err); - return done(); - } - done('error expected'); - }); + sp.validateSamlResponse(new Buffer(response, 'base64').toString(), {}, + function(err, profile){ + if (err) { + assert.ok(err); + return done(); + } + done('error expected'); + }); }); it('should validate an assertion from RSA IDM with no embedded signature and supplying a cert', function (done) { @@ -321,13 +320,13 @@ describe('interop', function () { var sm = new SamlPassport(samlOptions); var sp = new samlp(samlpOptions, sm); - sp.validateSamlResponse(new Buffer(response, 'base64').toString(), - function(err, profile){ - if (err) return done(err); - assert.ok(profile); - expect(profile['email']).to.equal('ricky.brown@emc.com'); - done(); - }); + sp.validateSamlResponse(new Buffer(response, 'base64').toString(), {}, + function(err, profile){ + if (err) return done(err); + assert.ok(profile); + expect(profile['email']).to.equal('ricky.brown@emc.com'); + done(); + }); function pemToCert(pem) { var cert = /-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----/g.exec(pem.toString()); @@ -358,7 +357,7 @@ describe('interop', function () { var sm = new SamlPassport(samlOptions); var sp = new samlp(samlpOptions, sm); - sp.validateSamlResponse(new Buffer(response, 'base64').toString(), function (err){ + sp.validateSamlResponse(new Buffer(response, 'base64').toString(), {}, function (err){ assert.ok(err); expect(err.toString()).to.equal('Error: Invalid thumbprint (configured: ANOTHER_THUMB. calculated: CD78CA598A6FB28A4D70EF6846C1141666A24240)'); done(); @@ -461,40 +460,4 @@ describe('interop', function () { s._authenticate_saml(req); }); }); - - - // it('should validate a saml response from datapower', function (done) { - // var SAMLResponse = '<samlp2:Response xmlns:samlp2="urn:oasis:names:tc:SAML:2.0:protocol" Version="2.0" ID="SAML-309e11f6-3b9e-4452-9db6-d3cf6cf99d9f" IssueInstant="2013-05-08T21:55:08Z" Destination="http://epolicy.azurewebsites.net/"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">www.axa-equitable.com</saml2:Issuer><samlp2:Status><samlp2:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp2:Status><saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="SAML-02cd7eb4-0117-4dee-bee9-31bf076e709e" IssueInstant="2013-05-08T21:55:08Z"><saml2:Issuer>www.axa-equitable.com</saml2:Issuer><saml2:Subject><saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">Christopher.Owen@axa-advisors.com.datastage</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData NotOnOrAfter="2013-05-08T22:55:08Z" Recipient="http://epolicy.azurewebsites.net/"/></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2013-05-08T21:55:08Z" NotOnOrAfter="2013-05-08T22:55:08Z"/><saml2:AuthnStatement AuthnInstant="2013-05-08T21:55:08Z" SessionNotOnOrAfter="2013-05-08T22:55:08Z"><saml2:SubjectLocality Address="10.74.68.219"/><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute Name="userid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue>3338532</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="firstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue>Christopher</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="lastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue>Owen</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="emailAddress" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue>Christopher.Owen@axa-advisors.com.datastage</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI="#SAML-309e11f6-3b9e-4452-9db6-d3cf6cf99d9f"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>7mSr1qvkKTrrV2bV+HFVJKpKaCI=</DigestValue></Reference></SignedInfo><SignatureValue>BQTRkGg8M/g2LpkgvW3MQG/B0cDxsz0zHa2wejIN2010r+hS4BCj7YcIH319R+Y4oZTmlxmJIT/4wlR9rxHQWxX95jdB1DfeoUMLAlk2JfAT+ByZ14F+N4B7lDILuNUTrDBNa9GLDIo8MyAK8SBUdeyqDtNFqb44/gGg6B0h2qEbDNLHY7WlAxvz4TfndKqk5v/VP96xiCS4d1AjYPvUL8EzR5kS83ABHX0jg2bYxdtctdiKOilcHQOHEIKosWw86b9uDQPjIrSt1JzW8SSSeA6M3nJ7HJ/5EsSYEMvt/FshBnKP2LI4HMGktlzw/9gOnmxR/CQykMmN2vvCwPsnXA==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIFQTCCBCmgAwIBAgIETB7lIzANBgkqhkiG9w0BAQUFADCBsTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9ycGEgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDkgRW50cnVzdCwgSW5jLjEuMCwGA1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxQzAeFw0xMzAxMzExNTM2MjBaFw0xNTAzMzAxOTA2MTJaMIGGMQswCQYDVQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMS0wKwYDVQQKEyRBWEEgRXF1aXRhYmxlIExpZmUgSW5zdXJhbmNlIENvbXBhbnkxIjAgBgNVBAMTGXJ0aWdpbnQuYXhhLWVxdWl0YWJsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCn6cGpvyzVSQ2c1oK7LzuxUXW4sxBOTGLVCqo4FWcta7QAU7RU5i3ATWxyo9HFmD8Qcyj6YuIQYtljJFAx/JcZigtXNRVudtl0uuvCUTGfsR67+gGRWe7hNg/9gIWLXZGkikRT4g9yBqutMzHeuX0ecignFHJxw5S7p1rtxJmuXQR/8uOeAse+48PkZcCHFdzBp6u3Z+pzite12LA2F8C0K5nv9FgkHVEQjqgtgAjKin2QmWqI1gj6mIe0oMxWB4l3j7dsEXVO4zPU1ujIylY0y2QnK4PbNdGu+W1GElwvUhSaz+jmIUFWklJtsFyhFVGcgFRE5iXXmrlnK4GZv3/FAgMBAAGjggGIMIIBhDALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50cnVzdC5uZXQvbGV2ZWwxYy5jcmwwZAYIKwYBBQUHAQEEWDBWMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAvBggrBgEFBQcwAoYjaHR0cDovL2FpYS5lbnRydXN0Lm5ldC8yMDQ4LWwxYy5jZXIwSgYDVR0gBEMwQTA1BgkqhkiG9n0HSwIwKDAmBggrBgEFBQcCARYaaHR0cDovL3d3dy5lbnRydXN0Lm5ldC9ycGEwCAYGZ4EMAQICMCQGA1UdEQQdMBuCGXJ0aWdpbnQuYXhhLWVxdWl0YWJsZS5jb20wHwYDVR0jBBgwFoAUHvGriQb4SQ8BM3fuFHruGXyTKE0wHQYDVR0OBBYEFJSL34z5hLkS08mEdEodVmeUrUC5MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADggEBAFCpXB2HagR+B4C5XKbtBPNZ3F94zJoFlCb+7/5Q9HWMGew1XiRx7GFhV4FCIsr6Gp9EYFLlVO+afdSMvNC7RauZ6nw7ylK8yRyuvbpJWC+XAfP4nVKroYYFPmKJdkELIBLIwt1Nsr3KsY0JIykokuQR/ ... [truncated]
test/saml11.tests.js+189 −187 modified@@ -1,196 +1,198 @@ var assert = require("assert"), - fs = require("fs"), - helpers = require("./helpers"), - saml11 = require("saml").Saml11, - SamlPassport = require("../lib/passport-wsfed-saml2/saml").SAML; + fs = require("fs"), + helpers = require("./helpers"), + saml11 = require("saml").Saml11, + SamlPassport = require("../lib/passport-wsfed-saml2/saml").SAML; describe("saml 1.1 assertion", function () { - it("should parse attributes", function (done) { - // cert created with: - // openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem - - var options = { - cert: fs.readFileSync(__dirname + "/test-auth0.pem"), - key: fs.readFileSync(__dirname + "/test-auth0.key"), - issuer: "urn:issuer", - lifetimeInSeconds: 600, - audiences: "urn:myapp", - attributes: { - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": - "foo@bar.com", - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Foo Bar", - }, - nameIdentifier: "foo", - nameIdentifierFormat: - "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", - }; - - var signedAssertion = saml11.create(options); - - var publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - var saml_passport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - }); - saml_passport.validateSamlAssertion( - signedAssertion, - function (error, profile) { - assert.ok(profile); - assert.equal( - "foo", - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" - ] - ); - assert.equal( - "Foo Bar", - profile["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] + it("should parse attributes", function (done) { + // cert created with: + // openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem + + var options = { + cert: fs.readFileSync(__dirname + "/test-auth0.pem"), + key: fs.readFileSync(__dirname + "/test-auth0.key"), + issuer: "urn:issuer", + lifetimeInSeconds: 600, + audiences: "urn:myapp", + attributes: { + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": + "foo@bar.com", + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Foo Bar", + }, + nameIdentifier: "foo", + nameIdentifierFormat: + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + }; + + var signedAssertion = saml11.create(options); + + var publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + var saml_passport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + }); + saml_passport.validateSamlAssertion( + signedAssertion, {}, + function (error, profile) { + assert.ok(profile); + assert.equal( + "foo", + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" + ] + ); + assert.equal( + "Foo Bar", + profile["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] + ); + assert.equal( + "foo@bar.com", + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" + ] + ); + assert.equal("foo@bar.com", profile["email"]); + assert.equal("urn:issuer", profile["issuer"]); + done(); + } ); - assert.equal( - "foo@bar.com", - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" - ] - ); - assert.equal("foo@bar.com", profile["email"]); - assert.equal("urn:issuer", profile["issuer"]); - done(); - } - ); - }); - - it("should handle unicode", function (done) { - var options = { - cert: fs.readFileSync(__dirname + "/test-auth0.pem"), - key: fs.readFileSync(__dirname + "/test-auth0.key"), - issuer: "urn:issuer", - lifetimeInSeconds: 600, - audiences: "urn:myapp", - attributes: { - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": - "сообщить@bar.com", - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": - "сообщить вКонтакте", - }, - nameIdentifier: "вКонтакте", - nameIdentifierFormat: - "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", - }; - - var signedAssertion = saml11.create(options); - - var publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - var saml_passport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, }); - var profile = saml_passport.validateSamlAssertion( - signedAssertion, - function (error, profile) { - if (error) return done(error); - - assert.ok(profile); - assert.equal( - "вКонтакте", - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" - ] - ); - assert.equal( - "сообщить вКонтакте", - profile["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] - ); - assert.equal( - "сообщить@bar.com", - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" - ] - ); - done(); - } - ); - }); - - it("should validate an assertion from office365", function (done) { - var signedAssertion = - '<Assertion ID="_1b1ffaef-86ef-42e1-92cf-cf8c9d9a4ce0" IssueInstant="2013-04-02T18:50:24.000Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/</Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /><ds:Reference URI="#_1b1ffaef-86ef-42e1-92cf-cf8c9d9a4ce0"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><ds:DigestValue>TzJmLs0BTPgpaPLsA7L2Kd9l1k4IBOmwIM/znV2iOPU=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>OHJCAffCNPRkwsE3RqnVPoCRSqsPrio8prABauzu2pqF418Y1QJuJehhzztY8A6kwnBUkBVE7BIyLe7kgCnBoNZWElYki1xtaLksc/Afc0TjlZvv9IJ9fQHIBiL1JA9KcySq1tu9dv/NauykBODXuljPuVTk6I4xLLWcg20o26Ov57axp42uWPpcJHtasomLmmmnAXEh6P7aB/1Vlm/MAJhWXToxacauJzFao3F9JNEuucKY6y3RPDp1Qq3vL0gq98RKuiaejayu6RjyyU2+8vCBzURul8b7ZXPUHfIOME6Q5LvbKqLhe/mzqRc+9GUg22X3B5SYjdnXjwHbBTbihA==</ds:SignatureValue><KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><X509Data><X509Certificate>MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng</X509Certificate></X509Data></KeyInfo></ds:Signature><Subject><NameID>10030000838D23AF@MicrosoftOnline.com</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" /></Subject><Conditions NotBefore="2013-04-02T18:50:23.969Z" NotOnOrAfter="2013-04-03T06:50:23.969Z"><AudienceRestriction><Audience>spn:408153f4-5960-43dc-9d4f-6b717d772c8d</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid"><AttributeValue>75696069-df44-4310-9bcf-08b45e3007c9</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"><AttributeValue>Matias</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><AttributeValue>matias@auth0.onmicrosoft.com</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"><AttributeValue>Woloski</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider"><AttributeValue>https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="2013-04-02T18:50:16.000Z"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion>'; - - var saml_passport = new SamlPassport({ - thumbprints: ["3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe0"], - realm: "spn:408153f4-5960-43dc-9d4f-6b717d772c8d", - checkRecipient: false, - checkExpiration: false, - }); // dont check expiration since we are harcoding the token - var profile = saml_passport.validateSamlAssertion( - signedAssertion, - function (error, profile) { - assert.ok(profile); - done(); - } - ); - }); - - it("should return error if validation fails", function (done) { - var signedAssertion = - '<Assertion ID="_1b1ffaef-86ef-42e1-92cf-cf8c9d9a4ce0" IssueInstant="2013-04-02T18:50:24.000Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/</Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /><ds:Reference URI="#_1b1ffaef-86ef-42e1-92cf-cf8c9d9a4ce0"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><ds:DigestValue>TzJmLs0BTPgpaPLsA7L2Kd9l1k4IBOmwIM/znV2iOPU=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>OHJCAffCNPRkwsE3RqnVPoCRSqsPrio8prABauzu2pqF418Y1QJuJehhzztY8A6kwnBUkBVE7BIyLe7kgCnBoNZWElYki1xtaLksc/Afc0TjlZvv9IJ9fQHIBiL1JA9KcySq1tu9dv/NauykBODXuljPuVTk6I4xLLWcg20o26Ov57axp42uWPpcJHtasomLmmmnAXEh6P7aB/1Vlm/MAJhWXToxacauJzFao3F9JNEuucKY6y3RPDp1Qq3vL0gq98RKuiaejayu6RjyyU2+8vCBzURul8b7ZXPUHfIOME6Q5LvbKqLhe/mzqRc+9GUg22X3B5SYjdnXjwHbBTbihA==</ds:SignatureValue><KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><X509Data><X509Certificate>MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng</X509Certificate></X509Data></KeyInfo></ds:Signature><Subject><NameID>10030000838D23AF@MicrosoftOnline.com</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" /></Subject><Conditions NotBefore="2013-04-02T18:50:23.969Z" NotOnOrAfter="2013-04-03T06:50:23.969Z"><AudienceRestriction><Audience>spn:408153f4-5960-43dc-9d4f-6b717d772c8d</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid"><AttributeValue>75696069-df44-4310-9bcf-08b45e3007c9</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"><AttributeValue>Matias</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><AttributeValue>matias@auth0.onmicrosoft.com</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"><AttributeValue>Woloski</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider"><AttributeValue>https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="2013-04-02T18:50:16.000Z"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion>'; - - var saml_passport = new SamlPassport({ - thumbprints: [ - "3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe1", - "3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe2", - ], // WRONG thumbprints - realm: "spn:408153f4-5960-43dc-9d4f-6b717d772c8d", - checkRecipient: false, - checkExpiration: false, - }); // dont check expiration since we are harcoding the token - var profile = saml_passport.validateSamlAssertion( - signedAssertion, - function (error, profile) { - assert.equal( - "Invalid thumbprint (configured: 3464C5BDD2BE7F2B6112E2F08E9C0024E33D9FE1, 3464C5BDD2BE7F2B6112E2F08E9C0024E33D9FE2. calculated: 3464C5BDD2BE7F2B6112E2F08E9C0024E33D9FE0)", - error.message + + it("should handle unicode", function (done) { + var options = { + cert: fs.readFileSync(__dirname + "/test-auth0.pem"), + key: fs.readFileSync(__dirname + "/test-auth0.key"), + issuer: "urn:issuer", + lifetimeInSeconds: 600, + audiences: "urn:myapp", + attributes: { + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": + "сообщить@bar.com", + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": + "сообщить вКонтакте", + }, + nameIdentifier: "вКонтакте", + nameIdentifierFormat: + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + }; + + var signedAssertion = saml11.create(options); + + var publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + var saml_passport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + }); + var profile = saml_passport.validateSamlAssertion( + signedAssertion, {}, + function (error, profile) { + if (error) return done(error); + + assert.ok(profile); + assert.equal( + "вКонтакте", + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" + ] + ); + assert.equal( + "сообщить вКонтакте", + profile["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] + ); + assert.equal( + "сообщить@bar.com", + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" + ] + ); + done(); + } ); - done(); - } - ); - }); - - it("should fail when the X509Certificate is invalid", function (done) { - const signedAssertion = fs - .readFileSync( - __dirname + "/samples/plain/samlresponse_saml11_invalid_cert.txt" - ) - .toString(); - const options = { - checkDestination: false, - thumbprint: "119B9E027959CDB7C662CFD075D9E2EF384E445F", - }; - - const saml_passport = new SamlPassport(options); - saml_passport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - const errorMessages = [ - "The signing certificate is invalid (PEM_read_bio_PUBKEY failed)", - "The signing certificate is invalid (error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib, error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error, error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header)", - "The signing certificate is invalid (error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error, error:0D068066:asn1 encoding routines:asn1_check_tlen:bad object header)", - "The signing certificate is invalid (error:0688010A:asn1 encoding routines::nested asn1 error, error:06800066:asn1 encoding routines::bad object header)", - ]; - - assert.ok(err, "The signing certificate was unexpectedly valid"); - assert.ok( - /signing certificate is invalid/.test(err.message), - "Error message is not the default invalid message" + }); + + it("should validate an assertion from office365", function (done) { + var signedAssertion = + '<Assertion ID="_1b1ffaef-86ef-42e1-92cf-cf8c9d9a4ce0" IssueInstant="2013-04-02T18:50:24.000Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/</Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /><ds:Reference URI="#_1b1ffaef-86ef-42e1-92cf-cf8c9d9a4ce0"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><ds:DigestValue>TzJmLs0BTPgpaPLsA7L2Kd9l1k4IBOmwIM/znV2iOPU=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>OHJCAffCNPRkwsE3RqnVPoCRSqsPrio8prABauzu2pqF418Y1QJuJehhzztY8A6kwnBUkBVE7BIyLe7kgCnBoNZWElYki1xtaLksc/Afc0TjlZvv9IJ9fQHIBiL1JA9KcySq1tu9dv/NauykBODXuljPuVTk6I4xLLWcg20o26Ov57axp42uWPpcJHtasomLmmmnAXEh6P7aB/1Vlm/MAJhWXToxacauJzFao3F9JNEuucKY6y3RPDp1Qq3vL0gq98RKuiaejayu6RjyyU2+8vCBzURul8b7ZXPUHfIOME6Q5LvbKqLhe/mzqRc+9GUg22X3B5SYjdnXjwHbBTbihA==</ds:SignatureValue><KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><X509Data><X509Certificate>MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng</X509Certificate></X509Data></KeyInfo></ds:Signature><Subject><NameID>10030000838D23AF@MicrosoftOnline.com</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" /></Subject><Conditions NotBefore="2013-04-02T18:50:23.969Z" NotOnOrAfter="2013-04-03T06:50:23.969Z"><AudienceRestriction><Audience>spn:408153f4-5960-43dc-9d4f-6b717d772c8d</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid"><AttributeValue>75696069-df44-4310-9bcf-08b45e3007c9</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"><AttributeValue>Matias</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><AttributeValue>matias@auth0.onmicrosoft.com</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"><AttributeValue>Woloski</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider"><AttributeValue>https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="2013-04-02T18:50:16.000Z"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion>'; + + var saml_passport = new SamlPassport({ + thumbprints: ["3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe0"], + realm: "spn:408153f4-5960-43dc-9d4f-6b717d772c8d", + checkRecipient: false, + checkExpiration: false, + }); // dont check expiration since we are harcoding the token + var profile = saml_passport.validateSamlAssertion( + signedAssertion, {}, + function (error, profile) { + assert.ok(profile); + done(); + } ); - assert.ok( - errorMessages.includes(err.message), - "Error message for invalid certificate is incorrect" + }); + + it("should return error if validation fails", function (done) { + var signedAssertion = + '<Assertion ID="_1b1ffaef-86ef-42e1-92cf-cf8c9d9a4ce0" IssueInstant="2013-04-02T18:50:24.000Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/</Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /><ds:Reference URI="#_1b1ffaef-86ef-42e1-92cf-cf8c9d9a4ce0"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><ds:DigestValue>TzJmLs0BTPgpaPLsA7L2Kd9l1k4IBOmwIM/znV2iOPU=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>OHJCAffCNPRkwsE3RqnVPoCRSqsPrio8prABauzu2pqF418Y1QJuJehhzztY8A6kwnBUkBVE7BIyLe7kgCnBoNZWElYki1xtaLksc/Afc0TjlZvv9IJ9fQHIBiL1JA9KcySq1tu9dv/NauykBODXuljPuVTk6I4xLLWcg20o26Ov57axp42uWPpcJHtasomLmmmnAXEh6P7aB/1Vlm/MAJhWXToxacauJzFao3F9JNEuucKY6y3RPDp1Qq3vL0gq98RKuiaejayu6RjyyU2+8vCBzURul8b7ZXPUHfIOME6Q5LvbKqLhe/mzqRc+9GUg22X3B5SYjdnXjwHbBTbihA==</ds:SignatureValue><KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><X509Data><X509Certificate>MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng</X509Certificate></X509Data></KeyInfo></ds:Signature><Subject><NameID>10030000838D23AF@MicrosoftOnline.com</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" /></Subject><Conditions NotBefore="2013-04-02T18:50:23.969Z" NotOnOrAfter="2013-04-03T06:50:23.969Z"><AudienceRestriction><Audience>spn:408153f4-5960-43dc-9d4f-6b717d772c8d</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid"><AttributeValue>75696069-df44-4310-9bcf-08b45e3007c9</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"><AttributeValue>Matias</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><AttributeValue>matias@auth0.onmicrosoft.com</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"><AttributeValue>Woloski</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider"><AttributeValue>https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="2013-04-02T18:50:16.000Z"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion>'; + + var saml_passport = new SamlPassport({ + thumbprints: [ + "3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe1", + "3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe2", + ], // WRONG thumbprints + realm: "spn:408153f4-5960-43dc-9d4f-6b717d772c8d", + checkRecipient: false, + checkExpiration: false, + }); // dont check expiration since we are harcoding the token + var profile = saml_passport.validateSamlAssertion( + signedAssertion, {}, + function (error, profile) { + assert.equal( + "Invalid thumbprint (configured: 3464C5BDD2BE7F2B6112E2F08E9C0024E33D9FE1, 3464C5BDD2BE7F2B6112E2F08E9C0024E33D9FE2. calculated: 3464C5BDD2BE7F2B6112E2F08E9C0024E33D9FE0)", + error.message + ); + done(); + } ); + }); - done(); - } - ); - }); -}); + it("should fail when the X509Certificate is invalid", function (done) { + const signedAssertion = fs + .readFileSync( + __dirname + "/samples/plain/samlresponse_saml11_invalid_cert.txt" + ) + .toString(); + const options = { + checkDestination: false, + thumbprint: "CDADA32647511D5F0E4676DE2DD8EE3FAD1F6D2D", + }; + + const saml_passport = new SamlPassport(options); + saml_passport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + const errorMessages = [ + "The signing certificate is invalid (PEM_read_bio_PUBKEY failed)", + "The signing certificate is invalid (error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib, error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error, error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header)", + "The signing certificate is invalid (error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error, error:0D068066:asn1 encoding routines:asn1_check_tlen:bad object header)", + "The signing certificate is invalid (error:0688010A:asn1 encoding routines::nested asn1 error, error:06800066:asn1 encoding routines::bad object header)", + // This is required for Node version 22 + "The signing certificate is invalid (error:1E08010C:DECODER routines::unsupported, error:0688010A:asn1 encoding routines::nested asn1 error, error:06800066:asn1 encoding routines::bad object header, error:0680009B:asn1 encoding routines::too long)", + ]; + + assert.ok(err, "The signing certificate was unexpectedly valid"); + assert.ok( + /signing certificate is invalid/.test(err.message), + "Error message is not the default invalid message" + ); + assert.ok( + errorMessages.includes(err.message), + "Error message for invalid certificate is incorrect" + ); + + done(); + } + ); + }); +}); \ No newline at end of file
test/saml20.tests.js+515 −462 modified@@ -6,496 +6,549 @@ const utils = require("../lib/passport-wsfed-saml2/utils"); const SamlPassport = require("../lib/passport-wsfed-saml2/saml").SAML; describe("saml 2.0 assertion", function () { - // cert created with: - // openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem - - const options = { - cert: fs.readFileSync(__dirname + "/test-auth0.pem"), - key: fs.readFileSync(__dirname + "/test-auth0.key"), - issuer: "urn:issuer", - lifetimeInSeconds: 600, - audiences: "urn:myapp", - attributes: { - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": - "foo@bar.com", - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Foo Bar", - }, - nameIdentifier: "foo", - nameIdentifierFormat: - "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", - }; - - it("should parse attributes", function (done) { - const signedAssertion = saml20.create(options); - - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - if (err) return done(err); + // cert created with: + // openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem - assert.ok(profile); - assert.equal( - "foo", - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" - ] - ); - assert.equal( - "Foo Bar", - profile["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] - ); - assert.equal( - "foo@bar.com", - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" - ] + const options = { + cert: fs.readFileSync(__dirname + "/test-auth0.pem"), + key: fs.readFileSync(__dirname + "/test-auth0.key"), + issuer: "urn:issuer", + lifetimeInSeconds: 600, + audiences: "urn:myapp", + attributes: { + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": + "foo@bar.com", + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Foo Bar", + }, + nameIdentifier: "foo", + nameIdentifierFormat: + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + }; + + it("should parse attributes", function (done) { + const signedAssertion = saml20.create(options); + + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + if (err) return done(err); + + assert.ok(profile); + assert.equal( + "foo", + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" + ] + ); + assert.equal( + "Foo Bar", + profile["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] + ); + assert.equal( + "foo@bar.com", + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" + ] + ); + assert.equal("foo@bar.com", profile["email"]); + assert.equal("urn:issuer", profile["issuer"]); + done(); + } ); - assert.equal("foo@bar.com", profile["email"]); - assert.equal("urn:issuer", profile["issuer"]); - done(); - } - ); - }); - - it('should ignore the NameQualifier validation when nameid format is "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"', function (done) { - const signedAssertion = - '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_ne0Z8R1z9xdbekXeWrAAg7srNB78exsb" IssueInstant="2016-08-02T21:54:04.971Z"><saml:Issuer>urn:issuer</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_ne0Z8R1z9xdbekXeWrAAg7srNB78exsb"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>p7iHnIt5xJZNimGNxh4d9R2J7DML8WNrMwMxmZ1WwSU=</DigestValue></Reference></SignedInfo><SignatureValue>pb1Wp/LFbigEj+TNm7gkAwlfIc17LNwUXVTgM8RQnMvYJfIPZbl1yo5xMCh6ObMFwCs1T+gKI5C7jMloX2QhWD/XUffBKiDfkZUg7NI/Jyt5m+Bdst12SNhHBVsNilL9ZCuf+QtQD7301gUhVHP6Ramf4y+XNod9AfzhFLYNfl6fhf/5KA/KkjiOwYW5Ps/43OMXXSeVaeQ7JRU8XqyKbwlB+YXGseFLnyZopv8Cw9Bb2935ADLX111oFBkiRhnMUJW0LMbSWM6UVJ4V0qoW9h+f3isN5+R87RECNeAQP3WSBiddnEuSdhgQYQVnb6s0mThpvs7uvIOlog0FqeSrvQ==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=</X509Certificate></X509Data></KeyInfo></Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameQualifier="name-qualifier">foo</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2016-08-02T22:04:04.971Z"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-08-02T21:54:04.971Z" NotOnOrAfter="2016-08-02T22:04:04.971Z"><saml:AudienceRestriction><saml:Audience>urn:myapp</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue xsi:type="xs:anyType">foo@bar.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><saml:AttributeValue xsi:type="xs:anyType">Foo Bar</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthnStatement AuthnInstant="2016-08-02T21:54:04.971Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>'; - - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - checkExpiration: false, }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - if (err) return done(err); - assert.ok(profile); - assert.equal( - "foo", - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" - ] + it('should ignore the NameQualifier validation when nameid format is "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"', function (done) { + const signedAssertion = + '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_ne0Z8R1z9xdbekXeWrAAg7srNB78exsb" IssueInstant="2016-08-02T21:54:04.971Z"><saml:Issuer>urn:issuer</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_ne0Z8R1z9xdbekXeWrAAg7srNB78exsb"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>p7iHnIt5xJZNimGNxh4d9R2J7DML8WNrMwMxmZ1WwSU=</DigestValue></Reference></SignedInfo><SignatureValue>pb1Wp/LFbigEj+TNm7gkAwlfIc17LNwUXVTgM8RQnMvYJfIPZbl1yo5xMCh6ObMFwCs1T+gKI5C7jMloX2QhWD/XUffBKiDfkZUg7NI/Jyt5m+Bdst12SNhHBVsNilL9ZCuf+QtQD7301gUhVHP6Ramf4y+XNod9AfzhFLYNfl6fhf/5KA/KkjiOwYW5Ps/43OMXXSeVaeQ7JRU8XqyKbwlB+YXGseFLnyZopv8Cw9Bb2935ADLX111oFBkiRhnMUJW0LMbSWM6UVJ4V0qoW9h+f3isN5+R87RECNeAQP3WSBiddnEuSdhgQYQVnb6s0mThpvs7uvIOlog0FqeSrvQ==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=</X509Certificate></X509Data></KeyInfo></Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameQualifier="name-qualifier">foo</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2016-08-02T22:04:04.971Z"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-08-02T21:54:04.971Z" NotOnOrAfter="2016-08-02T22:04:04.971Z"><saml:AudienceRestriction><saml:Audience>urn:myapp</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue xsi:type="xs:anyType">foo@bar.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><saml:AttributeValue xsi:type="xs:anyType">Foo Bar</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthnStatement AuthnInstant="2016-08-02T21:54:04.971Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>'; + + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + checkExpiration: false, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + if (err) return done(err); + + assert.ok(profile); + assert.equal( + "foo", + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" + ] + ); + assert.equal( + "Foo Bar", + profile["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] + ); + assert.equal( + "foo@bar.com", + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" + ] + ); + assert.equal("foo@bar.com", profile["email"]); + assert.equal("urn:issuer", profile["issuer"]); + done(); + } ); - assert.equal( - "Foo Bar", - profile["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] - ); - assert.equal( - "foo@bar.com", - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" - ] - ); - assert.equal("foo@bar.com", profile["email"]); - assert.equal("urn:issuer", profile["issuer"]); - done(); - } - ); - }); - - it('should ignore the SPNameQualifier validation when the nameid format is "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"', function (done) { - const signedAssertion = - '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_nPmKm9IskDCo61hUea4DX7o3POLbLcUK" IssueInstant="2016-08-02T21:59:48.654Z"><saml:Issuer>urn:issuer</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_nPmKm9IskDCo61hUea4DX7o3POLbLcUK"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>UYsy9NacnRqnSTbidM8WBBgS+Op0G05iBJTX0T1WlUk=</DigestValue></Reference></SignedInfo><SignatureValue>VzRQaMR5Qk1P+g1tqJRKroq4JJx00FZ0rZxO4vG2gGkBXJ8262B4VUHOkxyPHNH1l/DuxSnNsL8AAbZfn8EdxMdToPvm2hkqygyA5W20o6g6eSC41rDOavTzesOKoXn3Uq9DOiUXve5ieYYCt5bQcoSCVT6uhVEKMhdcLhaB507qj9Gzcfp0E4F57ezRTTnVVEF/wCJ5j0QTMA2Wh09fxNkGijlE8KHzDJZapN4tDCzmK8qY7211gKuTfKYJGXYA4hSxw9fiQGDEPKRYA6tWf0HO5Vd8edRg2ZHr7AgjuCPp5Fj8VOP+KppA1YFBbq4Eqqt6KHg91KJlGs3ivpmwPw==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=</X509Certificate></X509Data></KeyInfo></Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" SPNameQualifier="other-qualifier">foo</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2016-08-02T22:09:48.654Z"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-08-02T21:59:48.654Z" NotOnOrAfter="2016-08-02T22:09:48.654Z"><saml:AudienceRestriction><saml:Audience>urn:myapp</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue xsi:type="xs:anyType">foo@bar.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><saml:AttributeValue xsi:type="xs:anyType">Foo Bar</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthnStatement AuthnInstant="2016-08-02T21:59:48.654Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>'; - - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - checkExpiration: false, }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - if (err) return done(err); - assert.ok(profile); - assert.equal( - "foo", - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" - ] - ); - assert.equal( - "Foo Bar", - profile["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] - ); - assert.equal( - "foo@bar.com", - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" - ] + it('should ignore the SPNameQualifier validation when the nameid format is "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"', function (done) { + const signedAssertion = + '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_nPmKm9IskDCo61hUea4DX7o3POLbLcUK" IssueInstant="2016-08-02T21:59:48.654Z"><saml:Issuer>urn:issuer</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_nPmKm9IskDCo61hUea4DX7o3POLbLcUK"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>UYsy9NacnRqnSTbidM8WBBgS+Op0G05iBJTX0T1WlUk=</DigestValue></Reference></SignedInfo><SignatureValue>VzRQaMR5Qk1P+g1tqJRKroq4JJx00FZ0rZxO4vG2gGkBXJ8262B4VUHOkxyPHNH1l/DuxSnNsL8AAbZfn8EdxMdToPvm2hkqygyA5W20o6g6eSC41rDOavTzesOKoXn3Uq9DOiUXve5ieYYCt5bQcoSCVT6uhVEKMhdcLhaB507qj9Gzcfp0E4F57ezRTTnVVEF/wCJ5j0QTMA2Wh09fxNkGijlE8KHzDJZapN4tDCzmK8qY7211gKuTfKYJGXYA4hSxw9fiQGDEPKRYA6tWf0HO5Vd8edRg2ZHr7AgjuCPp5Fj8VOP+KppA1YFBbq4Eqqt6KHg91KJlGs3ivpmwPw==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=</X509Certificate></X509Data></KeyInfo></Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" SPNameQualifier="other-qualifier">foo</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2016-08-02T22:09:48.654Z"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-08-02T21:59:48.654Z" NotOnOrAfter="2016-08-02T22:09:48.654Z"><saml:AudienceRestriction><saml:Audience>urn:myapp</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue xsi:type="xs:anyType">foo@bar.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><saml:AttributeValue xsi:type="xs:anyType">Foo Bar</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthnStatement AuthnInstant="2016-08-02T21:59:48.654Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>'; + + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + checkExpiration: false, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + if (err) return done(err); + + assert.ok(profile); + assert.equal( + "foo", + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" + ] + ); + assert.equal( + "Foo Bar", + profile["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"] + ); + assert.equal( + "foo@bar.com", + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" + ] + ); + assert.equal("foo@bar.com", profile["email"]); + assert.equal("urn:issuer", profile["issuer"]); + done(); + } ); - assert.equal("foo@bar.com", profile["email"]); - assert.equal("urn:issuer", profile["issuer"]); - done(); - } - ); - }); - - it('should validate the NameQualifier when nameid format is "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"', function (done) { - const signedAssertion = - '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_dZZwXmRpwYcaIdFA5l0qSCCuu0if9UNo" IssueInstant="2016-09-29T12:34:13.488Z"><saml:Issuer>urn:issuer</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_dZZwXmRpwYcaIdFA5l0qSCCuu0if9UNo"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>ndqj65JwACeDERG2aZF6k0IF85KshkhgILzxhbKRyiw=</DigestValue></Reference></SignedInfo><SignatureValue>LPcIU9W9HmX1QM+baMPLTj9StBFRksnDoFn/HVd8uLJgdH8Xeiv9TOQGElmSaBLypjCeN6ILq6pcZ0mxMC9zfd9X3WKmYtcrGI1BugATeNsqUm63x+Msau8pNuZrNNbfIQvLooMhF4T92ym2ADSm+zCQVNwBH7/v0rVIE6MEy8AYqqfpvH9CR88XQYMCSgKN0JQ2FPbcHvhIX7Hl+xG6PSzgfznE8dcWBUi24FajyGpqlNm8O3uHCfjR3wzO42UQIJFOJOiLb7QGNyWE1KXKYWyzZgAxGQuRUcbYxcnKTbVK3b3TBH+p2ZR+a2ktKmvqNBvQxy6tE4UXDIIpvmknSw==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=</X509Certificate></X509Data></KeyInfo></Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" NameQualifier="invalid-value">foo</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2016-09-29T12:44:13.488Z"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-09-29T12:34:13.488Z" NotOnOrAfter="2016-09-29T12:44:13.488Z"><saml:AudienceRestriction><saml:Audience>urn:myapp</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue xsi:type="xs:anyType">foo@bar.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><saml:AttributeValue xsi:type="xs:anyType">Foo Bar</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthnStatement AuthnInstant="2016-09-29T12:34:13.488Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>'; - - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - checkExpiration: false, }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - assert.equal( - err.message, - "NameQualifier attribute in the NameID element does not match urn:issuer" + + it('should validate the NameQualifier when nameid format is "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"', function (done) { + const signedAssertion = + '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_dZZwXmRpwYcaIdFA5l0qSCCuu0if9UNo" IssueInstant="2016-09-29T12:34:13.488Z"><saml:Issuer>urn:issuer</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_dZZwXmRpwYcaIdFA5l0qSCCuu0if9UNo"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>ndqj65JwACeDERG2aZF6k0IF85KshkhgILzxhbKRyiw=</DigestValue></Reference></SignedInfo><SignatureValue>LPcIU9W9HmX1QM+baMPLTj9StBFRksnDoFn/HVd8uLJgdH8Xeiv9TOQGElmSaBLypjCeN6ILq6pcZ0mxMC9zfd9X3WKmYtcrGI1BugATeNsqUm63x+Msau8pNuZrNNbfIQvLooMhF4T92ym2ADSm+zCQVNwBH7/v0rVIE6MEy8AYqqfpvH9CR88XQYMCSgKN0JQ2FPbcHvhIX7Hl+xG6PSzgfznE8dcWBUi24FajyGpqlNm8O3uHCfjR3wzO42UQIJFOJOiLb7QGNyWE1KXKYWyzZgAxGQuRUcbYxcnKTbVK3b3TBH+p2ZR+a2ktKmvqNBvQxy6tE4UXDIIpvmknSw==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=</X509Certificate></X509Data></KeyInfo></Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" NameQualifier="invalid-value">foo</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2016-09-29T12:44:13.488Z"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-09-29T12:34:13.488Z" NotOnOrAfter="2016-09-29T12:44:13.488Z"><saml:AudienceRestriction><saml:Audience>urn:myapp</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue xsi:type="xs:anyType">foo@bar.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><saml:AttributeValue xsi:type="xs:anyType">Foo Bar</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthnStatement AuthnInstant="2016-09-29T12:34:13.488Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>'; + + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + checkExpiration: false, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + assert.equal( + err.message, + "NameQualifier attribute in the NameID element does not match urn:issuer" + ); + done(); + } ); - done(); - } - ); - }); - - it('should validate the SPNameQualifier when the nameid format is "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"', function (done) { - const signedAssertion = - '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_muhd3ZBmN9LK3Ev08rkc8CA40YvNOkGl" IssueInstant="2016-09-29T12:35:22.345Z"><saml:Issuer>urn:issuer</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_muhd3ZBmN9LK3Ev08rkc8CA40YvNOkGl"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>Gsh2DubHXYDeuHyBa+DU1k5G43UjyQsyPRYVgEqpTD8=</DigestValue></Reference></SignedInfo><SignatureValue>mubsqLCaM16gT2rAFLl8XDuLWTALH6cdRMM/kNHLpVzO5PA6FGVPX5ojW2UCKGOhGHn0Hd/mYCOCtgAROphWjxpQl5TDyeQE0frjKs8ik0V/Jjy5T6PeWKHLqN6sHbP6YpkGixshCWtop8JOs6SijM9PBGnWal6Nx5bUMBfAyUnGyIhLwNaE8Z4NHkyAqmdxgLS0e8w5qngKQaUlyERZCrqKQ4w8VaHFG4Dos36XVfh+U7udo3IlbrpBLsu9xG1Azxe2iPC6+84xC09P6EvQylvTnz5NU4jhk10SmmRjZ6AVvzJTz/gbVfRfZoEAUhCuIIh3HuRwf400ESf38ouZTA==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=</X509Certificate></X509Data></KeyInfo></Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" SPNameQualifier="invalid-value">foo</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2016-09-29T12:45:22.345Z"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-09-29T12:35:22.345Z" NotOnOrAfter="2016-09-29T12:45:22.345Z"><saml:AudienceRestriction><saml:Audience>urn:myapp</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue xsi:type="xs:anyType">foo@bar.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><saml:AttributeValue xsi:type="xs:anyType">Foo Bar</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthnStatement AuthnInstant="2016-09-29T12:35:22.345Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>'; - - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - checkExpiration: false, }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - assert.equal( - err.message, - "SPNameQualifier attribute in the NameID element does not match urn:myapp" - ); - done(); - } - ); - }); - - it("should parse attributes with multiple values", function (done) { - options.attributes = { - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups": [ - "Admins", - "Contributors", - ], - }; - const signedAssertion = saml20.create(options); - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - if (err) return done(err); - - expect(profile).to.exist; - expect( - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups" - ] - ).to.be.an("array"); - expect( - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups" - ] - ).to.include("Admins"); - expect( - profile[ - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups" - ] - ).to.include("Contributors"); - done(); - } - ); - }); - - it("should validate expiration with default clockSkew", function (done) { - options.lifetimeInSeconds = -240; - - const signedAssertion = saml20.create(options); - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - expect(err).to.exist; - expect(err.message).to.equal("assertion has expired."); - expect(profile).to.not.exist; - done(); - } - ); - }); - - it("should validate expiration with overriden clockSkew", function (done) { - options.lifetimeInSeconds = -240; - - const signedAssertion = saml20.create(options); - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - clockSkew: 5, - }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - expect(err).to.not.exist; - expect(profile).to.exist; - done(); - } - ); - }); - - it("should should allow expired cert if option not passed", function (done) { - options.lifetimeInSeconds = 10000; - - const signedAssertion = saml20.create(options); - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - // The embedded cert is expired, so we can use this as is. - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, + it('should validate the SPNameQualifier when the nameid format is "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"', function (done) { + const signedAssertion = + '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_muhd3ZBmN9LK3Ev08rkc8CA40YvNOkGl" IssueInstant="2016-09-29T12:35:22.345Z"><saml:Issuer>urn:issuer</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_muhd3ZBmN9LK3Ev08rkc8CA40YvNOkGl"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>Gsh2DubHXYDeuHyBa+DU1k5G43UjyQsyPRYVgEqpTD8=</DigestValue></Reference></SignedInfo><SignatureValue>mubsqLCaM16gT2rAFLl8XDuLWTALH6cdRMM/kNHLpVzO5PA6FGVPX5ojW2UCKGOhGHn0Hd/mYCOCtgAROphWjxpQl5TDyeQE0frjKs8ik0V/Jjy5T6PeWKHLqN6sHbP6YpkGixshCWtop8JOs6SijM9PBGnWal6Nx5bUMBfAyUnGyIhLwNaE8Z4NHkyAqmdxgLS0e8w5qngKQaUlyERZCrqKQ4w8VaHFG4Dos36XVfh+U7udo3IlbrpBLsu9xG1Azxe2iPC6+84xC09P6EvQylvTnz5NU4jhk10SmmRjZ6AVvzJTz/gbVfRfZoEAUhCuIIh3HuRwf400ESf38ouZTA==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=</X509Certificate></X509Data></KeyInfo></Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" SPNameQualifier="invalid-value">foo</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2016-09-29T12:45:22.345Z"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-09-29T12:35:22.345Z" NotOnOrAfter="2016-09-29T12:45:22.345Z"><saml:AudienceRestriction><saml:Audience>urn:myapp</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue xsi:type="xs:anyType">foo@bar.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><saml:AttributeValue xsi:type="xs:anyType">Foo Bar</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthnStatement AuthnInstant="2016-09-29T12:35:22.345Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>'; + + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + checkExpiration: false, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + assert.equal( + err.message, + "SPNameQualifier attribute in the NameID element does not match urn:myapp" + ); + done(); + } + ); }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - expect(err).to.not.exist; - expect(profile).to.exist; - done(); - } - ); - }); - - it("should validate certificate expiration with embedded cert", function (done) { - const signedAssertion = saml20.create(options); - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - // The embedded cert is expired, so we can use this as is. - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - checkCertExpiration: true, + + it("should parse attributes with multiple values", function (done) { + options.attributes = { + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups": [ + "Admins", + "Contributors", + ], + }; + + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + if (err) return done(err); + + expect(profile).to.exist; + expect( + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups" + ] + ).to.be.an("array"); + expect( + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups" + ] + ).to.include("Admins"); + expect( + profile[ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups" + ] + ).to.include("Contributors"); + done(); + } + ); }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - expect(err).to.exist; - expect(err.message).to.equal( - "The signing certificate is not currently valid." + + it("should validate expiration with default clockSkew", function (done) { + options.lifetimeInSeconds = -240; + + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + expect(err).to.exist; + expect(err.message).to.equal("assertion has expired."); + expect(profile).to.not.exist; + done(); + } ); - expect(profile).to.be.undefined; - done(); - } - ); - }); - - it("should validate certificate expiration with non-embedded cert", function (done) { - const signedAssertion = saml20.create(options); - - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - // The test cert is expired, so we can use this as is. - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - checkCertExpiration: true, }); - const parsedAssertion = utils.parseSamlAssertion(signedAssertion); - assert.equal( - parsedAssertion.getElementsByTagName("X509Certificate").length, - 1 - ); // Make sure we start with exactly one embedded cert - const embeddedCert = - parsedAssertion.getElementsByTagName("X509Certificate")[0]; - embeddedCert.parentNode.removeChild(embeddedCert); - assert.equal( - parsedAssertion.getElementsByTagName("X509Certificate").length, - 0 - ); // Make sure we removed the cert(s) - - samlPassport.validateSamlAssertion( - parsedAssertion, - function (err, profile) { - expect(err).to.exist; - expect(err.message).to.equal( - "The signing certificate is not currently valid." + + it("should validate expiration with overriden clockSkew", function (done) { + options.lifetimeInSeconds = -240; + + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + clockSkew: 5, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + expect(err).to.not.exist; + expect(profile).to.exist; + done(); + } ); - expect(profile).to.not.exist; - done(); - } - ); - }); - - it("should validate recipent", function (done) { - options.lifetimeInSeconds = 600; - options.recipient = "foo"; - const signedAssertion = saml20.create(options); - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - recipientUrl: "bar", }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - expect(err).to.exist; - expect(err.message).to.equal("Recipient is invalid. Configured: bar"); - expect(profile).to.not.exist; - done(); - } - ); - }); - - it("should extract authentication context from assertion as a user prop", function (done) { - const options = { - cert: fs.readFileSync(__dirname + "/test-auth0.pem"), - key: fs.readFileSync(__dirname + "/test-auth0.key"), - issuer: "urn:issuer", - lifetimeInSeconds: 600, - audiences: "urn:myapp", - attributes: { - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": - "сообщить@bar.com", - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": - "сообщить вКонтакте", - }, - nameIdentifier: "вКонтакте", - nameIdentifierFormat: - "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", - }; - const signedAssertion = saml20.create(options); + it("should should allow expired cert if option not passed", function (done) { + options.lifetimeInSeconds = 10000; + + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + // The embedded cert is expired, so we can use this as is. + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + expect(err).to.not.exist; + expect(profile).to.exist; + done(); + } + ); + }); - const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, + it("should validate certificate expiration with embedded cert", function (done) { + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + // The embedded cert is expired, so we can use this as is. + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + checkCertExpiration: true, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + expect(err).to.exist; + expect(err.message).to.equal( + "The signing certificate is not currently valid." + ); + expect(profile).to.be.undefined; + done(); + } + ); }); - samlPassport.validateSamlAssertion( - signedAssertion, - function (error, profile) { - if (error) return done(error); - assert.ok(profile); + it("should validate certificate expiration with non-embedded cert", function (done) { + const signedAssertion = saml20.create(options); + + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + // The test cert is expired, so we can use this as is. + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + checkCertExpiration: true, + }); + const parsedAssertion = utils.parseSamlAssertion(signedAssertion); + assert.equal( + parsedAssertion.getElementsByTagName("X509Certificate").length, + 1 + ); // Make sure we start with exactly one embedded cert + const embeddedCert = + parsedAssertion.getElementsByTagName("X509Certificate")[0]; + embeddedCert.parentNode.removeChild(embeddedCert); assert.equal( - "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified", - profile[ - "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod" - ] + parsedAssertion.getElementsByTagName("X509Certificate").length, + 0 + ); // Make sure we removed the cert(s) + + samlPassport.validateSamlAssertion( + parsedAssertion.toString(), {}, + function (err, profile) { + expect(err).to.exist; + expect(err.message).to.equal( + "The signing certificate is not currently valid." + ); + expect(profile).to.not.exist; + done(); + } ); + }); - done(); - } - ); - }); - - it("should fail when the X509Certificate is invalid", function (done) { - const signedAssertion = fs - .readFileSync( - __dirname + "/samples/plain/samlresponse_saml20_invalid_cert.txt" - ) - .toString(); - const options = { - checkDestination: false, - thumbprint: "119B9E027959CDB7C662CFD075D9E2EF384E445F", - }; - - const samlPassport = new SamlPassport(options); - samlPassport.validateSamlAssertion( - signedAssertion, - function (err, profile) { - expect(err).to.exist; - const errorMessages = [ - "The signing certificate is invalid (PEM_read_bio_PUBKEY failed)", - "The signing certificate is invalid (error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib, error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error, error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header)", - "The signing certificate is invalid (error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error, error:0D068066:asn1 encoding routines:asn1_check_tlen:bad object header)", - "The signing certificate is invalid (error:0688010A:asn1 encoding routines::nested asn1 error, error:06800066:asn1 encoding routines::bad object header)", - ]; - - assert.ok(err, "The signing certificate was unexpectedly valid"); - assert.ok( - errorMessages.includes(err.message), - "Error message for invalid certificate is incorrect" + it("should validate recipent", function (done) { + options.lifetimeInSeconds = 600; + options.recipient = "foo"; + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + recipientUrl: "bar", + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (err, profile) { + expect(err).to.exist; + expect(err.message).to.equal("Recipient is invalid. Configured: bar"); + expect(profile).to.not.exist; + done(); + } ); - done(); - } - ); - }); - - describe("validate saml assertion (signature checks)", function () { - it("should fail when signature is not found", function (done) { - const assertion = - '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_muhd3ZBmN9LK3Ev08rkc8CA40YvNOkGl" IssueInstant="2016-09-29T12:35:22.345Z"><saml:Issuer>urn:issuer</saml:Issuer><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" SPNameQualifier="invalid-value">foo</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2016-09-29T12:45:22.345Z"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-09-29T12:35:22.345Z" NotOnOrAfter="2016-09-29T12:45:22.345Z"><saml:AudienceRestriction><saml:Audience>urn:myapp</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue xsi:type="xs:anyType">foo@bar.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><saml:AttributeValue xsi:type="xs:anyType">Foo Bar</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthnStatement AuthnInstant="2016-09-29T12:35:22.345Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>'; - - const publicKey = fs - .readFileSync(__dirname + "/test-auth0.cer") - .toString(); - const samlPassport = new SamlPassport({ - cert: publicKey, - realm: "urn:myapp", - checkRecipient: false, - }); - samlPassport.validateSamlAssertion(assertion, function (err, profile) { - assert.ok(err); - assert.ok(!profile); - assert.equal( - err.message, - "Signature is missing (xpath: /*[local-name(.)='Assertion']/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#'])" + }); + + it("should extract authentication context from assertion as a user prop", function (done) { + const options = { + cert: fs.readFileSync(__dirname + "/test-auth0.pem"), + key: fs.readFileSync(__dirname + "/test-auth0.key"), + issuer: "urn:issuer", + lifetimeInSeconds: 600, + audiences: "urn:myapp", + attributes: { + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": + "сообщить@bar.com", + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": + "сообщить вКонтакте", + }, + nameIdentifier: "вКонтакте", + nameIdentifierFormat: + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + }; + + const signedAssertion = saml20.create(options); + + const publicKey = fs.readFileSync(__dirname + "/test-auth0.cer").toString(); + const samlPassport = new SamlPassport({ + cert: publicKey, + realm: "urn:myapp", + checkRecipient: false, + }); + samlPassport.validateSamlAssertion( + signedAssertion, {}, + function (error, profile) { + if (error) return done(error); + + assert.ok(profile); + assert.equal( + "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified", + profile[ + "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod" + ] + ); + + done(); + } ); - done(); - }); }); - it("should fail when signature is found twice", function (done) { - const assertion = - '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_muhd3ZBmN9LK3Ev08rkc8CA40YvNOkGl" IssueInstant="2016-09-29T12:35:22.345Z"><saml:Issuer>urn:issuer</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_muhd3ZBmN9LK3Ev08rkc8CA40YvNOkGl"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>Gsh2DubHXYDeuHyBa+DU1k5G43UjyQsyPRYVgEqpTD8=</DigestValue></Reference></SignedInfo><SignatureValue>mubsqLCaM16gT2rAFLl8XDuLWTALH6cdRMM/kNHLpVzO5PA6FGVPX5ojW2UCKGOhGHn0Hd/mYCOCtgAROphWjxpQl5TDyeQE0frjKs8ik0V/Jjy5T6PeWKHLqN6sHbP6YpkGixshCWtop8JOs6SijM9PBGnWal6Nx5bUMBfAyUnGyIhLwNaE8Z4NHkyAqmdxgLS0e8w5qngKQaUlyERZCrqKQ4w8VaHFG4Dos36XVfh+U7udo3IlbrpBLsu9xG1Azxe2iPC6+84xC09P6EvQylvTnz5NU4jhk10SmmRjZ6AVvzJTz/gbVfRfZoEAUhCuIIh3HuRwf400ESf38ouZTA==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQM ... [truncated]
test/samlp.functional.tests.js+123 −93 modified@@ -2,7 +2,7 @@ var expect = require('chai').expect; var request = require('request'); var qs = require('querystring'); var cheerio = require('cheerio'); -var xmldom = require('@auth0/xmldom'); +var xmldom = require('@xmldom/xmldom'); var fs = require('fs'); var path = require('path'); var zlib = require('zlib'); @@ -28,19 +28,19 @@ describe('samlp (functional tests)', function () { before(function (done) { // this samlp request comes from Salesforce - doSamlpFlow(`http://localhost:5051/samlp?SAMLRequest=${samlRequest}&RelayState=123`, - 'http://localhost:5051/callback', function(err, resp) { - if (err) return done(err); - if (resp.response.statusCode !== 200) return done(new Error(resp.body)); - r = resp.response; - bod = resp.body; - done(); - }); + doSamlpFlow(`${server.BASE_URL}/samlp?SAMLRequest=${samlRequest}&RelayState=123`, + `${server.BASE_URL}/callback`, function(err, resp) { + if (err) return done(err); + if (resp.response.statusCode !== 200) return done(new Error(resp.body)); + r = resp.response; + bod = resp.body; + done(); + }); }); it('should be valid signature', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); it('should return a valid user', function(){ @@ -57,18 +57,18 @@ describe('samlp (functional tests)', function () { before(function (done) { server.options = { signResponse: true }; - doSamlpFlow(`http://localhost:5051/samlp?SAMLRequest=${samlRequest}&RelayState=123`, - 'http://localhost:5051/callback/samlp-invalidcert', function(err, resp) { - if (err) return done(err); - r = resp.response; - bod = resp.body; - done(); - }); + doSamlpFlow(`${server.BASE_URL}/samlp?SAMLRequest=${samlRequest}&RelayState=123`, + `${server.BASE_URL}/callback/samlp-invalidcert`, function(err, resp) { + if (err) return done(err); + r = resp.response; + bod = resp.body; + done(); + }); }); it('should return 400 (invalid signature)', function(){ expect(r.statusCode) - .to.equal(400); + .to.equal(400); }); }); @@ -77,19 +77,19 @@ describe('samlp (functional tests)', function () { before(function (done) { server.options = { signResponse: true }; - doSamlpFlow(`http://localhost:5051/samlp?SAMLRequest=${samlRequest}&RelayState=123`, - 'http://localhost:5051/callback/samlp-signedresponse', function(err, resp) { - if (err) return done(err); - if (resp.response.statusCode !== 200) return done(new Error(resp.body)); - r = resp.response; - bod = resp.body; - done(); - }); + doSamlpFlow(`${server.BASE_URL}/samlp?SAMLRequest=${samlRequest}&RelayState=123`, + `${server.BASE_URL}/callback/samlp-signedresponse`, function(err, resp) { + if (err) return done(err); + if (resp.response.statusCode !== 200) return done(new Error(resp.body)); + r = resp.response; + bod = resp.body; + done(); + }); }); it('should be valid signature', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); it('should return a valid user', function(){ @@ -107,18 +107,18 @@ describe('samlp (functional tests)', function () { before(function (done) { server.options = { signResponse: true }; - doSamlpFlow(`http://localhost:5051/samlp?SAMLRequest=${samlRequest}&RelayState=123`, - 'http://localhost:5051/callback/samlp-signedresponse-invalidcert', function(err, resp) { - if (err) return done(err); - r = resp.response; - bod = resp.body; - done(); - }); + doSamlpFlow(`${server.BASE_URL}/samlp?SAMLRequest=${samlRequest}&RelayState=123`, + `${server.BASE_URL}/callback/samlp-signedresponse-invalidcert`, function(err, resp) { + if (err) return done(err); + r = resp.response; + bod = resp.body; + done(); + }); }); it('should return 400 (invalid signature)', function(){ expect(r.statusCode) - .to.equal(400); + .to.equal(400); }); }); @@ -128,7 +128,7 @@ describe('samlp (functional tests)', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback' + uri: `${server.BASE_URL}/callback` }, function(err, response, body) { if(err) return done(err); r = response; @@ -139,9 +139,9 @@ describe('samlp (functional tests)', function () { it('should redirect to idp', function(){ expect(r.statusCode) - .to.equal(302); + .to.equal(302); expect(r.headers.location.split('?')[0]) - .to.equal('http://localhost:5051/samlp'); + .to.equal(`${server.BASE_URL}/samlp`); var querystring = qs.parse(r.headers.location.split('?')[1]); expect(querystring).to.have.property('SAMLRequest'); expect(querystring).to.have.property('RelayState'); @@ -154,7 +154,7 @@ describe('samlp (functional tests)', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback/samlp-with-invalid-xml', + uri: `${server.BASE_URL}/callback/samlp-with-invalid-xml`, form: { SAMLResponse: 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9mbWktdGVzdC5hdXRoMC5jb20vbG9naW4vY2FsbGJhY2siIElEPSJfNzY4NjU5OGUzNDk4YjcxOGM3MjcyNmZlMjVhZDU3Y2MiIEluUmVzcG9uc2VUbz0iXzM3ZjAyNjJkYWZlNmJhZWFmYThiIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDQtMTFUMTE6MzU6MjQuMDYwWiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSI+aHR0cHM6Ly9hYWktbG9nb24uZXRoei5jaC9pZHAvc2hpYmJvbGV0aDwvc2FtbDI6SXNzdWVyPjxzYW1sMnA6U3RhdHVzPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PHNhbWwycDpTdGF0dXM+PHNhbWwyOkVuY3J5cHRlZEFzc2VydGlvbiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgLz48eGVuYzpFbmNyeXB0ZWREYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIgSWQ9Il9jOGY1Y2QyZTAwY2UyMzkwYTJkMjdlMzRjZjQwZWI2YSIgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRWxlbWVudCI+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI2FlczEyOC1jYmMiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIvPjxkczpLZXlJbmZvIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48eGVuYzpFbmNyeXB0ZWRLZXkgSWQ9Il8wZjczNDk4NTFkMjY0NDk2NWE0N2M2ZjU2OTc1MDk1MSIgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjcnNhLW9hZXAtbWdmMXAiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIiB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIvPjwveGVuYzpFbmNyeXB0aW9uTWV0aG9kPjxkczpLZXlJbmZvPjxkczpLZXlJbmZvIC8+PGRzOktleUluZm8gLz48ZHM6S2V5SW5mbyAvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSURPekNDQWlPZ0F3SUJBZ0lKQVBQb0hyRXBiN291TUEwR0NTcUdTSWIzRFFFQkJRVUFNQjB4R3pBWkJnTlZCQU1URW1adGFTMTANClpYTjBMbUYxZEdnd0xtTnZiVEFlRncweE16QTFNRFl5TXpBek1UZGFGdzB5TnpBeE1UTXlNekF6TVRkYU1CMHhHekFaQmdOVkJBTVQNCkVtWnRhUzEwWlhOMExtRjFkR2d3TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUtiOUdpZmYNCitLdlFQd285ZW9PYlNXN05OWVpGclVveFJuNzRxTWNkZlp3a3V3RzNPOEVHaThYK1VzTnROZ3dRbE1WZll0OWxLQjcxaUpTbEtPQmkNCkJQU0ZQN3pQOWpGdFRuZkpjYVJ2ZHZZUG9JQzRZODF0dTZMa05OM2UxLzMxTnArUjZwZDdGNkxmSFdxdWYrQitoeUhKQ1hhc2RkNkoNCmxHb2ViOTQrZW1wajlsbTh3SE5iM3NyLzg4Mzk0S0ozRlVCZXhQelE1cnBLTGU3ZDVmbTRFS08vaXlFcFdIVWxmN2RmOXlHRDZtNzENClB4bys4cjhEcXE3QTVFaEdYL3prNlN1d1o0ai9zeml6eW4vY1h1bGxHZzNQQXNjOVhYTFQ0NTVBMUtFQng1ZVRHck1jN0pRM3VEVXENCnFmRGY0dmp3bE5CY0lqeGcyWDNkTTBzSlZrLzVyMDBDQXdFQUFhTitNSHd3SFFZRFZSME9CQllFRkJzNWxwZnZleU9Tb3BtTlZlZWgNClhQK1BHdGszTUUwR0ExVWRJd1JHTUVTQUZCczVscGZ2ZXlPU29wbU5WZWVoWFArUEd0azNvU0drSHpBZE1Sc3dHUVlEVlFRREV4Sm0NCmJXa3RkR1Z6ZEM1aGRYUm9NQzVqYjIyQ0NRRHo2QjZ4S1crNkxqQU1CZ05WSFJNRUJUQURBUUgvTUEwR0NTcUdTSWIzRFFFQkJRVUENCkE0SUJBUUEyWmszU21TR1RPaC82cmF6ZW0vRmk4R3pFb3BjS2RJRTF1ZUNoVHNBemg2L21pbTVxNWxIMFBXMWI4NXNRMy9jMzFTWVUNClN4VlpCODRLMk1QNitod0MwV1p4a3E4eTBpTUVFQXhXeUMzWjNpOXBTbEdkdzdzdi9OV0p2NFlQam8yc1NOSHVaODBPMTFhM2NYb3UNCll4TE84REJSTXE5VlRzN1JiN3FLRkJXbDVJeCtjWnhWZ2xyeEl2NlcwOE9ycm1xUGVvRGp1aUppQmoyOGNzamhlaFlFbEtZY25VNEwNClJkSWpCbFpGbjFBb1RKUkJGQXlqTDhCdlNNSU1Sa3pFcm8vR3AzSXpqNjAzUkJUR09rdm5pYWxLSGN3TG5WRkZFMHhlWlpVcTdLdzANCkx2TzBYOHVTM0RYN2RUYzJvcXpYT1R4NDIvT2o1cTl4VXVhaXVYME1SWlQwPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PHhlbmM6Q2lwaGVyRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiPjx4ZW5jOkNpcGhlclZhbHVlPlBtN2dVRDI5d1AwMTdLUnZKTmdmVzUxRkQ0eHlUTGJ5RDdXbElNRVZUR2xzWnc5K3ZNbUdzL2VkdXJoT2ZVZEV2SGZBV04vdUYzYkxCOTl1Q1pFN0dHLzJ0aDVBS2pLejFaN1NvZWZuUU54dnFvbXUyNUNmWTEwUzFpbitNMU13N3ZrcTZlS0c4bndEQjBDc3JsOXJ6ZUMyekNQRFc1TG81N0x2NDNNbUVpM1dYZkVhbkQwZDJZT2NRVFppaHIzUlpnajl0SDJUQmVKZjhNN28yY1BrOXFBWk40aU56dk1oWE5OV0RDR256SGxIdXNxVk9RNWM4d2l5MmwzdWlUZlk3aEIvTVhpUDVmemRPYitEbWw4NlJrT2MyUVd3RHUwQ0t1ZHBveVRxQW90OUhFZ1JoL25tVXVSQkVKQ3NtWEdyN3FOM3ZSWW5HTXRmdEswZG9VYzN6QT09PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjwveGVuYzpFbmNyeXB0ZWRLZXk+PC9kczpLZXlJbmZvPjx4ZW5jOkNpcGhlckRhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpDaXBoZXJWYWx1ZT4xY1JHdFhpWFZMYXRhSHFTMWVLM3JtTjlQcnJyZ0laR25XM0xPT25aektuU2w2dWFKWnRENXFyQkRURE5zaXg2ek15cHJPOU5KQVlQYlJEQ21CbWNaVStMSm5aSVdtT3IrQ29SdWJBUGdIT2Q3Q0VoQVB6RGVJZFh0S0V3R29hNHNKbXJRMjlCR3VSejBYS3ljTlJhU2lJa0toRm40dTZCRzV0a2dzS0RRT3Q3dmd0RzhuV21pczZqM0lYSVdrWGpXT2tKWjBHbktXajJqd3EwL3ArdEo1OXM1R3lTQi9uVy9nQW1PcjlLelhJMllQWm5CTk1xSUl6ZTZPNDBaaDRtaXpmUmdTSzM4dE5XQ3NCRlB6Q1krZjJWZVlsMzVORythdUhEZU9BRGtkQkR4d2xqRTJCV0Q2MXBKaFhuRzBTRVBIcnV1b1k3YldjWGpoNDVYLzhiM1hNcHF4ZDFjRFJPZkZMdFRMb0syVjVqcnhiRGRvcVh2TFR2NnZ5NDZGVkZYRVNEV0xmSGFxTjBOTlNsY2hORXJmcFV3SDRNUXNVOGU1UW1yeEw1QUVDVldmQytEYnNOZTVjZFhZcS8wVTBkcWJPV1ZlQkdVL3h0KzJEWmVhSFBTZVpweGhCb3diZ0FKejRlTlpQdnhIWVRGZHducXZhemRneWo4VUdHbEQvZjFoSXFHWHFja3BNbTJ2OEplZUd1akV6SDNnMXNOcEluWG40eVBaTUFLSEtOaVhIZTF3aWk1V2V5Ty9tMHlCUGZreGRrMll1S3NPMy9KWDlOcXltNXV6WUIzbmZZN1JkR21pdXBDV2tpb05aL1hidG5Bb1dDSENyc210am1kdm5rM2NBTVAvdmNSWmluQm9pSjgxdnBLdTNtSm5FdGV6cEVqaUMxY1dnTUxoLzZRK29MbXZOL1dyNVZWZmxjSFJYMWNhTEQrWURncnkybTVCbHBTRjhVKy9KYXR6NjR3Y0V2aXlqVFgwelN6VlRtakptZG44a3FvSEN0V3RvVCtWQUt0MG9CcGRMdHg5eDUzUTVTVG1rUG5xOXdnc3FWRXBGZGFHK25qM1RiZkY5K1FBMHFCVk1BVmxpZmNyb01YTzZRZlpFZUwwNWpFZWFSUGZpK0t5K2tVNUUvMFduTWFVUU1ZZE1kUFh5NW1XenZuZHMzVE5DczRjMWhYc1c2SXFoeTRnQjFPdEFVdjhIeEtPWVhrT0ZMMVN4TEs4Ty80UUUwVEFGd0hvZ0hEY3NVTEIvNDJHZ3IxZjZJVWcyTGNjOTMvN3JOUkhOV21OK21ienBJdUUvT0lRTEM5RW5yM1ErSkdURFJKajJsR0Fidzk4MnlsUzYyS2ZETDlKMWEzdzdtb0ZKM3ZubTdTUmZ4K25CREw2YzFBSDJTS0s1SmsrT3VDOWVSWllwMDdXMy9TQnJOWFVUUzhpbnpRaERLSWwvOVJ0b1Bzbyt3K3RnaCtIY2svNERaQnV0cmtpY3BGejBWZm5pK0VGZXBEZVJLT1ZwY1JNclVFeHdvWFZ3SitBUldMRnBIdDFJQjh4TmJTeFU4WUpzeDlMdWxGeXhUQzVOYWFqU0N4WFhkRENFajE5U2o2MFRYZGcwOHNRY0NYdGxUS1dLVDdQQXl1cGo5dFZtdVg2NkEweGQwdWt3MmhjbSs1bnB4REtEbUhKdWFMSUlzSDB4WDFyQVE0a3VqVHg1aGNGZnEyMDZIK2JBUE1xTEYyTjFDSnlkYUJMd2xoejRwa0tPczhMRXVzTGtySGVUK3hFeHBETnk4MzBZQ3M1SUpnTkY2R3dFcFdiaklGbVNIMlpzSnBycnB4b3h6eGtuZ29DSzYvSlRkYUVoM0diWkVmaFNlSW5kTGFnUktFcjZ5azNFMS9sNzlxTmxCbzlpUndTRE5UVXdkMmVHOENhZVpyYjJQZEIweDk2L3Z4L2lkUXNwQjNCQjkrbzBRaFhndVp5c0pDMmtIdmZUSndZZjRWVDBoL01TQnk1dGVWYU1kUWZxUmpCVm9pbGZncnFaVjlPTHZOOUw3TzUxVGFqVE1EcmdleGxIV3VNNVhMczZUK3pNdXM3WisrNys2aGNEUGg4UXpxY2ljVyt6TGpOTHN3OXhZS09CNlJ4T1NHTy9CekkxcExhbmRHSnJ5bXVsWlBicUJyZEQ1MXFWNUxwWjVBcUU2MHg5RGc0NDNJRkpPeWIxUzN3VCsyb3BBVEFDVDZQV3o0blJsNHZJc2J1M1RlVGdGTXZiUHdKdmV0U1d2ZjNwckdRNDlqVHpMaXhlSUlRTmUyOXdYamZROE1TKzJSUGppMnlpQVFCcllITWo2UzBiWFloYUVCRmFGN1kxNTRxMVJORk5rTmpmN2tVQjBTbVlnaVU4em83QjdLNzRwTm1SbWxIcitMcGtFcHVyVEU5Lzgvd1hhRWFzSitIb0UyNVlTYnFZRitxKzQ1L1NFcEJQdWpCa2ZQdkQwR0pqVCtnbGxiNzFWQU9nMTk2dGN2RFU4TUduSUN6SGM0ajJRaWVhZkF2YzZMNElvMEJRQXhnNDZXTTlIWVJyOGhZeURQNzFCMC95Sm5CV1R5QzUvWFNsRmsrQWFQeXpxdFZpMk5GeFh3bnRldzA1R1NjU2F2NDdKSEFRVFZaRlJsd2k4RkNtaUpKZWVQVWRrb2FlZExOT21oUXFMN0hmZ000RDRmOE9MaHhxdng3Ym5QQkRPMmlBcndYbHFrdy9Lc3l4dlQvZVd3NUZMOW41KzVDTWxiYnJCTEpNM2o2WkVxM3RJOWZuYk1vNmFHUERRb2RURUMwaEY2ZHdJYm02VG5tdFlLcDFZSTR5UWlzMUFURmIxbTY4R0htS0tidXNiMGE4N3BKbzRYbDlQdVdLSFRhMXBWdDBVTWw0RWJjNm14aXdyRytIZzNFdDRXUk5qaTJHQXhWMmYyekFaN1ZsNXpGRk02dmdxbFlqQ3NWZWNLekM1emp6T2Y4aDd0UThKdTUxMWJpRjlhN090cFhCWHZqVWdvcVRFV25SM1pyRjI4MkNNV3VvUXJHbjlwNFR2eldORU1iWjd2cGJiQ2tYS0t3cFhhWXNzL3pwcHg2d3JXWTRNOUdoVE45RVhXTTRXUGVpbmZ1MStUQUdDK3dISFVTd2h2TE9Ob0RsaHhqcU94MVVweVZubmxTeTV2QlBIbWRrYXgzT3o4MWhIY3hHVEtvb3N1U0hRYU9hL2xRelY4Sm9sM2RyZURobjlBWkM1bjRsa3hYV0tUOEhuQUNMS3VUTE4rMXpQUVY0UWorSDcxNUloNkZzNkE3bGY0dVloY2pWcWNVdWZrTHFvcjd0NjdOck5FekIyNSs2UVNRSlhMbTNUU2M2UTlCUDNjNUhWOGZzVHJEL043RUdJUkJGL3BTNldSMzY2UjV6WkJiWjZXbFNOek1TcnlwZnlQVDk5ODhJeGtLRFZqYjE1MkZ3Njg4OTZvUS8rckdxY3BxYlB5bmsvYkpVbThWWU1vbzA3U0J4Y2hURncybmtRc1VLT0J2MkdJT01WQ0d0YUVmWDh5YVBlcGZ4UGFxSm5tVjJTQ2U3Q09vVWQ1b3g2SzgrL21uWUZaTXJ6N3RYQ1VIU1MybDdyU3BYTStXZ013VnIwalh5OXdYSkxKK3NLM0dXSDZYVDAzNHZrMk1KZHdmcU9zQnBwTkFjeStNekd5NDlFVlFULytQb2FlT245MzM3aHBibjFnVmNMd2ovWGMzRll4REtkakY1OGNUL2d0dG52aWRhaktUcGNnQ1l4R1F2KzNRUkhidWFEWnRyRXY0L3phNnpiRGZHcEtzbklqeVVhc21HeHpiQ1Q3ZjFMQU1aYlJwaklnYi9ScVRnVnhEQXRhUGNSVU9YTEE3N29ZZm83Y2h2STd5RlJGbk9lMDNURDhHRFFLaGI5VUlzN1JmRXNYS0xVOVZwZVp3NnZ4Q1lNZDhScTlDbG5kOG1jR0FsNDgrOTRVeUFWZUdUelYzMWVCam44QjQ1R0wzWDkxL2JULzExaHZNS05YMjFQbUZ5eVR5YzZNMXhVY3IzN282YWY0SEFzVW5YZ1o4U1VLUXdKUE1WQzkrRW52RHByUjYvWS81S1l5MFVnb1IrR01IRTNaMXl0UUdZWEZZRlVaVW1NVjJVZ3oySW5pVTRhWExYNFlxQjhMRVgvRjVENnUzcVFCekhkSXhPelNTdlRiRXN6dVhuOFd4NStMNW1MVGM1U1JuNmxQZTRIUVZ1U0lLYzg3Ym5UTDFzNHFadjF0KzlaTUxLSVRHc1J4bjM1YXFZdVBMdDVOM053MlBZUkg4ZHFyZkwvWlA5WW5CWXo2aEFlWXZQVG5pYWF2ZVYzU0dNeW1VRVM5YlVtbHRjOUo0NnR3MkNwYkkxT0NDRDJ3dW1ucTlvbkZEam1oQUticENkaVNlSXFyNll4Y09QbzFXTnNVMmR1Z1Z3L1dYbTl5RUJ4S0o1UHJjNHJhdjhPT2Ezc0g0Z0cyYm9KeEMrMTluenZDbnVmTTRiRVQ3WVY5SVdmaDJLOGJacytRYTZPYjBTOXZnUWw1YWhGekpCUGhNUW91NjhKLzNkQm5ScUlPUENUMUhxcDN2aktlR3B4bGQxRDl3azZNYUREakl4aTJvWDQ3azZYREt6UkprUENabnJFTDZSblRodFJ0dzhiRUJCblZvdTdHWlZMZWtmRmw1MGlHTldBTmJpYTFGaDhad0c3SmxGbnhQZkhOdHYwM1QrT0dmOGhnMDV4YVlWQjRtZURKeC8rWmJBQzBKYUpYeUpNMnJmeTVuY3UxTk9BNWdEV1E3Vkp0MlFyQXh2cmQ5TVV4eHRnbHRtek1Nc1FZbDd3QVByMk5QRW1Nck1EeXJCcVRNY0JWbVg2N05sNGhBck44WUQySE9WZW5pZkkzcGFZSXMySTg3eWJ4SVNsMlEvbDFySXdScHFZSFVzRGNqVjBmUzd5c2VVZUdnK2hEMDdZR1RWaVkvT0JHeEswMzJneUtUcFdVMHFibW8rWTYvWkdVdnBqbldCRWJ5QkhkZ0xia0JUL29CNVE0azdLMi9TQXd6N29DZStJWG9OYkxYYkFNSGZIS296WFNxUysxQkN1TUUxSnNsWGpSWFk1WWJnRms2MU8zUndLRFVMaExUNUdmSlVFUVNoU2h2S05naHYzOUlBY1o2YWlFWlp3WVBUK1BudnNpc1hKanFiM2lhaU1yMCs5aEIvR3hrOHN5amRuVU1mL3FJOEpRU0k5UVhIZ0t6UU5pY0dSZFdjNUpXUkZreUIxMzh5dUF6NHQ4eHI1SzFhNStBUHJ6M1JJem1KWExpSlBySkhhcnlTL1ZNclBKaHR4WFpoYVlsYlJPRFptaUJjci9LVGlaVk02OWNlK3ZTa2FtZHFCakxFTThNWHZXQzRUY1poSDUzZXlmblJNUjIyb3hKVEVlQjZDYWJFZ3diNjNtQXlVb2JUSkdabFlvZ2NpbTZXZ3MwWnFDNllhaVR2dzNuLzEwWnhHWE5XRnNMRGhDa08vTGxqQWh2SXo0VElnN2F1UCsybXRNc2pram9IVDRDMG5QcXhGREZHanVRRHJpblRDZU1ZRGtnVTBhRTZtMzNnRGp2UEZDbGNqTEc4bTd4REtKbmwzTDNsVCs1eUNJSTNvY2NqaGxpTGVlN3JzSklkeFFaeEd3aDB0RUtmSThtdGYxbzlOSnllMEc1UnpXOTdiOXk5T0hEaVloTWNtalFHVHA5ZE54a0h2cU9qak9QbkdBL1FYZDcwYWVHVy9PT25MUjdBdkRzS1owbENrZDN6dFh6eVVLZ0VwQWRHK2FSbkNsT1JodG9xZXJwN1VxUHJvTmRGdHVNWStnOTlyWU9XS0RxSEo0ZFBBK3lRTkFRTHVBNTBLMU5RMHgzbEdlSlpaaTQraCthN1RRdDF4UVNqODBWVk4zR0VJc1I3WE02RXRNa0JLa2h0NjZoS1pQejJpT29mSVl1YUl0QkJiUWRZN3RXTW9LMElUL2ttbVMrK25MSmJiOUh2WDdqMGxvVWhqVFdocHF1MDQzcHRVeVBvWlVEaEVicmVsVzJldW9kc2ZuNHhoN1A1VUlaR2FNVU5HZ2k1bnF6a2p6MXVzaDEzU3huN0g1NTdZeGlZV2p2TmVJWmd6ZGJWK0dKdERDczBoVm0xZzhpbDh2MnEyazViLzlRZWh1NVVpTFNVZ3AvYVp6d1hOL0xkajQyVWxpWHM4TWRPOHVRVFRnZzZDb0dEbVcvaGJZZU1BV3NFelBlUGliRENmU0t0c0gzTlNGWWRUU0hFdjJYUU0zOEkvL2JpTlR1VWhSNzYxT0U5Qy9ndVFmd1lnbkRJZ05uV0NPUTdYRXRqSHZ2SDM2OUZiSEtnNlpwVmlzRWlEMk1qYnpCbEJsTm5FTFFBU3dQOVVBME1iWVQ2L0xuS3hYTnJXTDRwYWs5WjA0R3FBTm0ydTVxcFp5WGZFZzVGWXdUODVHbUpmSW9JQ2lCSk1JeHE0bXlwdTZ4dWRPdnhPeVVCZFE2enQ0dDR4MHp0TWQ3KzlNN3YzdjNjTVZOVFh0WG9NUEZLL1NKQWRaSGVxb2ZIS0FtcEJiaWV2ODhWL05XaVR5SVVUT1c3bkxGdWpSYUtPODZmWnB5c01kKyt5Q0crcEJDZyswZFY5d3RuYjhkdnl5d2hENkoxTExoelQydGZhUU5mWHZ3VUw1d255M3R3Zkd6M3NhV1A2YkYrSmpZd000SlltYjdEbUNySTZML3M5WlJ1TWlGclRFa3JqNUJ0T2oyckpObFlwZ1NWZ2Z2WWtLWDhmZFVhdWczRWRzVktheThkNzRwUE11S3gza0g3N0ZWbktUdWNiTEdSS1AyU1FuT1RvbE1ZTHNlQUhOK2phUkNpODJldTRFVzBxbWNmYjJNM2JjNVBzd0s3UUVQSWVnYW02Sm5jMFhOeUVPU2NWU1hmUUZEU3FGME1jSkVjeVUyQmIwaFgvTStNazd3SEFRUjBxa2tOQU5KYXkrbFAvQVliWVJ1dUtPWTdXRUxheHUzM0dhZ3pZeU5scWJFR1BQQnBTU2tTbCtiMXEyZHRXWGhmd3JXV052Z3lnMUQ4eVN5U2pJUXNaTEUyS1pab2RuZ2labUtqUERjd3pDMFBvcENSVXQ2ZFBZM3ExWTJjMk9Bd1BpY2NkN0YrYWlSSjdzNWpPei9BOHp5NlpLdll1ZW8xSDdoaVd2dFlJZ2YrQy9MaFFuQ0UxR21rODNzdURPQ3JQSUxPMDduOEcrMjZoenEvQ3lGKytGdHNyb29XUTErb29TanNmQWpXaDZ1QUR2N29DbmN3OFdmdjJXVk5pcGVHZU13UlJpcTFuVWFGN3BwK3dONGdsU3NVZzZ2V21GdHQ0QURFeXp3ODBMRExPNGszeXpsQURmc3pJeFc0KzNsT3FKN2loaklvSkMzRlB6djRWNXE5VWFlWjB2NWt2bk5YTmZUb3k0eG95dFROcUVTODFZYjZiK3R5R0pqZFRyai9WZ3l1dEU1b2oyN3R4YU9ObGloYzhObDlQbzZZb3ZrK3BFUU1vMEQ4SlVXNi9qL0FKUnVTT0I1MllMN1BJK3FFQXpIY0tYTXg4d3l3bEVHWEdrako5QXBoWTA4MUFyazJWclA3UEJEd2xwcENlQUtjZ1R3dTNtN0lRL1FibzFQMVhwMlB6U1hVbnI2N0xiRmFTT3ZjalNtNGlBQ3FjeXVPaDFVb0pIRGRRZWJ6UTdrWEdiQ1R6bEVPZ1NRazl5RzJMY25mOGVaVHBvRXZTbUpRNGcyTEovRkpjelBheGI5cUZXaTF0cGt2OVFrd04vTTVSVWd6QmNjNjV2ZUZQZE1ES09VRmU0UnNtV3htbFo2ZXBwQmJxVk5CbFhoQVBIWSs1aXRjWVR3eHVLUGJNRUNjcDYwc01xUEEyMDdoOWl1WVdvclRMMmNyMjQxSlBDQ3lvZVR5TDZ6MjhmbisyQXR5akxCVC8ydXpaUm9iejY4dzNDRUpLU1paQVlURG16ZWpaWXdVbmkxQ3BhM2JEcDNZcHd6ZkFIeEtSbmtwblJUS2gvUmFYVy95RElZb0U5eHQrcFBjb0kycXRLWGZpMjcxRXQ5eUMwTElvRllFQ2dQRE1GRGp2dGZlQTlnS2lpbWNiUThSSGdld3c2c1VLRS81L0VJcGlRS2t0UC9oSUtna2VHbmkxSXJHbnJYNU5aRmVlN1R0V3BlK3ozZUhCRFdJNlh4TGx6a3Y4ZzdQS1ltSkpxZzY2Unl5K1UzSlBkbk9pSkxoTWRzSmd2NDRlNm55am5VK1V5RWRpN3VGTUdoaFdOUlFSNWg0Y1JRMTB4MXMyNUdyUzhZWDlOdWVZNk5nMlAzYUJ4R2E0enZMa3YrSzA2czN3dUl2d2piSWxkdmpqdTM5di80eTlWMlo3VGdRWlFVUVMzQUd5NmFmQ296VXNnN3pFQitOZmx5U0ZwUDQ4eGY2U0NHWFcrbXZ6NVVabWlEMUErNkFlRlBmTktFd1lWZFB2OXN2aElaWlJ6cVQ4RTRNNCtaVUlxS202ZGplRHp1QmdGaEV4djNZNzZlNDd6OFhCa1p0SFV6aHptNlZPTW1rT2JualFlekdWTTBVdnlGbzVYWVZQbzhTQlo5TVBqd1NVbm9CUzB2T01Zb3BSdTBDM1BQR2NVSVFuZ1BncGF2WVRlTHpTWXZzU055WEwySnEvRUd4eWJUekYwN01Kek44VDN6SURUcE9FUjRUOGVGMEl6czVLcERSdFV6c1JLQjFwNm5RcnFmRFdTcE9VRDB1cXU1U3pPTE8vZkJrb1V5QWlWaldtMUFiNWxtSjZWMW4zRHJLazQzVVBMeTJ2MmFMWWZsMWR0QXFkSHBqY2dwaVdLQVptVCtBMU8zMFRLQlVQR3lnTlN5bW45ZGI2ZkJFQWphTG9USG9DS0QrR1RzNTgvTThpb3htM1NUd0F3K1FJUjAyV3RYTjJGM0JpQWV2Z1pGR0xRNEk3eVVvZ1RXdzh2VnFiY0pZWEZOclF2NDl0eVRBcjhRWnhpamIyM1p2Y01EM2ptcDBNbURoTGZza212T0d4WkdLeW9ERzk3b0k4cW1mbzFjQjVxMVUzSk1NSDFudEFiZTl6MWZFTVNOeVpCenVMV3QyQk9MTWU3akNQR0pYSThmKzUrcllmVGJEVlRxeldQK3lrN1pFSVF5TW9kcitoSlE2eEQ4Tm83Q255KzFpMlR0cU1wTGF5YlowUDVDRzlNQ0h6eHMwbHp6bjJiRVFyUmUxVlg5TUJ3NHVmNFFCeUZZZ1phUmN2NzdNdis1alV5QnQ0amhVd3dxdWp3eTRjL3VYdytQQU9iMHBsZ2s0SG8yUDcvUm10bmE0bnZBQWo2Rzl2cVBVS3pqTnh3RmRkRWxFb1ZERDUrK3Q3TnhhYVVXZzljRjhLRFJZU0M0enUxbXNXQXVYZFQ4aEd2Q1lmNjd4bTc1SVB1SnRtZ3FCWmpYUjg3b0NKb3h3aXZ5bzh0dHRxbUFaOFp4K0VCY1hqV2pLMzVKMzZvVFZ3U2JOcnFJYjZLbmpHbEZYSVN3clBORWkrUmtMb3RhU3lPU3ZuS251bERjM0doaEVYYXNCY3dEMElPNzNpZEk0YmlnR2F1bW9sSFF4QWVMRytVOUZycUY4dGdRT1dMenY0aHd3SlFHL2NpNE5GYmZISWpEVnM0bXM5WTdzQldUNkI1T1pNTzZyRE1wOUlsZG1Fcm1VUGE4ejU1ZVdqMDQ1YUxEQkt0Q1k5UHpXaHZXWHhqeTQ3SmxCU2ZETVBNVFhReHJ0T1B1SjRLSmo1V05xTlg3WXoxaTJMTm5weG5KbnltOWNsVTdGV1MrT2dUeTNBOWpYaTFCVXg0QjBHZzhXWUl4LzVBd0pzU0RPVC8yV0NpM09jRXhtUVRveVZrQ2p5UE55ZWNOck9GQmRQenowa1NaQjNJaFU4dW5OT1N0YytaNjdOZnF5K0kvRTlaVDczcUNCYVRJN1UxSFZxQzlsSnluUk1zbUZmMUdvbGNROG5DbXZvK0tPVTVtVkVzL2xvQThIZ3pWZWVNR043SkxaOFhJcUVkbnp5NHQyNlNRbTlobVY2ZDRiS2ZjQmk0MG9XNGZRUFdZRG1pWlorR1MrTDhuSnlYdzNBWkRIWkdKWEhFci85S096RFF0OVU0WVdpYS9NMTIrbFNyWmd5QmlrdWxnbnBBYmU0ZERpMUZMQTZMMUQzQjBIUlozRUdHamRRbng1R1hFZXhnNG9CVlQzS2dRbmpweXJrN0YzR0tQOUhGdnFLTUZ6VWJoL2RCV3k5bjRWc2JXUFlkSDdsSDFyVUg0YzdwN0lrWGc0VlREOE11MzV3b3ZORkErWkVBYmR3Z3o2dTR6NXZpZzhIcHNiUVpNVXZFVy91VE83cVU4UW56Z1o1NHRHcGl2YyszbnBhYnhSUTZSMDNpMHZYdDZCb0JJckFBMndxMmxzMTVSTXhleFZWczdYT25ORDRlcHFoMUJzR2pROC91ejZya1dRNVJFenNCeERxbXpZSTA1UXhYZlducUM2WVloWXpON1FQUzh0SGpYbGxsN0VzKzhVYzhzTytjRkE3REM3Y0hRbVZGaVpXN1NZYmFtTmFHc2FpY2VYTHE4Q3YveDJIZ21oUzYveU1qUFVFVkhoOTVZdHdhc25kT0p5NGdBbUlEWkh0d2RsOWxXY1ZDMDl4MUlBakt5UWlpQjRPLy9DcG9qZjFOUmNiUWF3VVBKMGI5ZXJyWDNFVWM3cU5Vc2NGMlpubXZVVThxM1g2UFVpb2p5TDVGMUFNdWJzcThuOTFmaG5FUmwrY2syQUJWYm9yeEhORlpSVXgvVlNYNHRMZzROUG1VcmhlalNoNXJZUkl6S3Q5OG9PNTBsTlEraS9RVkZUbmt6Ym9SUkkrS0NTM1o1bzJZZ0hlOGx2ZGhDeHYxZ3hjVGhRM21pd2dIeDdmR09LV1Myc2psSkxYanMxbm9raVowTEZ0NTIxaWYzKzFSYVZlRHpCU1hYRFFZVCtwbEI1S0M2L0ZOZEpDWW9mZGVTY0gwK0lHN2FyeWRyOUhUOU5SMXNHczZnc2JYQnNnWnViNXFKVmYzSisvQ3kra2FCYzByTEtyZ3UzZmlGUG1uMDdhWGdtK2xRVjBPVlVkaWVoNm5vQ0oxcHczT2VNdWJ0TU9rcmQyUWRFNEQ3T1ZoVithdUM5b1pIc09uQXhDYnZoYXovTTZYOEFaNXducHUvZVdwdDZ2WHBhWHpXVklJcVBET0NFMnRCSTcrNlNmTHdjVXFlYU5ZSVhtUlRwNnRUOGhhcDduZXpDZldqUGZxU2ZMVGljaFpEU1dlR2hYTXZWNTRCODJ2eWJoM1NBNXZCZDdwRnkzWTlxYnNCaHZDbHRKWmwzK044OXNnbTNJWElyeU5MT3JmL3VUMVJ0NDRrVDNleFM5N3FCWVpteGZXZW5PeUxqOHAxZGJlSjd6MlFxeE1xZXRia3FOUHBucnduMUFMV3VkSU5ZOGhLYnJYS0RDQmhWMmNQTkg3VWdTcnZYVFV2T1UxRnVyQ0FGRThSa09vcWxobUZ4SXRRUHUzNWd4MWFqaVFBTkMxQllTdlkvZzZyL2lLMHRlekFoRGNtaW4xVWF3Qk1qMkZHK2M4ZXdCYzVVbzN6ZUNIdERYVnlsSlY3UUdGVGZZR0JOWmlKUXJkYnFXRmEra01tdjRJUWYzSjY5bjBEeVNwRTJVY0h5dlFKQ3d0czlUN3NMU2h6WWFNVFVKUUFmOHhwcjBXKzVac3NHMjhJcXFsSTNkbUFpM0dQa2REblE3c0ZiNXBWSVVFVHRUQm9kMEp3K0U1UGgvY0Evbk9FbHpRYXRXUmUxWCs3NFl0bzdZSWJBRW56NW1YSGlva1hnTkVzbEdUWXhzWUo5QVBWOSt4WjVKTk1VWFl0OGhrVHBwaFBzN0FlazFpOXhsMW5sbm5FYnY0M3hnM2VMYTRySDBBcy8wTE9GK01yV2Z0aDBoWnQxanJCb05ycmNHOG1WWVJyd29YaXFIS3NSZTRVYTQ4a0YvRXVRWFZKeUZPV05KNUJjYW9xOSswa3lJdnVoeVhxNS9UL0tJTG12cFpTMzZINHJ1bFJhRm1Hb29sejBtaUl4V2FrdHFpUjF1VjhYaWtiekZFUzR2cGV5NHVKMWVBZjUyRCszL2NLY0V6V2E2KzU2VGRFQ0VKQWExWDl2Ni83UzFDZlBxaVkwN1lNMXZXdXNHcEhPQ0FSTFBDWGZ5V2ZXQytVSThPM1FkSEhRaC9XNXpzZ3RFWnVhYWhkWk9lY2VsQmtnS0NIZ1o4NTFpN2IvY25EN2VsRHlCVGJuNHFKRWhSWVRLWUNRZW82SEpFd1N5b3NhOVJrU0lpQklBaGVHaTlSRWJYY2xMY1JnR3FoMXp5Qi9JUHFmMUtXOXFMbzhzaU9wRUlUU3BOeks3NSttMlc0L2c4TjJLTGgvb1MyeGEyTXZGU0pTemdZZmpwYjlOK3pVeE5XR0xjZDVQR2J6STdkYW4yY2tKSmE2UkhLRG1WTWtqYW1ZTHh6M0ZKK29SdUFhTzFpcjMzWGViSGV6ZHErQjRKT2FweVhleGs4M1hIbG1kS211UTg1aWdqRmZPVElHMmFJc2FXcTdDL0ppZmdyK0pqdVRoSm55TVdyYkNSRTV2VkZ0MEdJYWlvR1NsYktIM1RYUnhaK0tXdXozZ3ErWXIzSWNrT1RIRUgwR0FERy9pc2lQSWV4bHJHNmRZaW43V20vUGVkRGVzd0t5TFlwVFMxejNzVGVTSXRFbXpZSERNRURzS0FsNVh5anZOalJvNzg5OHVhRW5pMWIraWpCc2p5b01EY0RFaHcxZzhseWpIZldrSjRwRitIWGpuMzJBak9qb0ZuNVIrcWM1ZWpsTVVxV0N1OUU0UWJTZWtWbmcwSW02OG9naWF5WVVDQTlZQklSd0Y5b3l2REZ5WGRJblhFOFFsZDRkYks2dUpwM2xzbERkclNWczBZQTdxRnBQY0MrWWdFZnBEa2NraXVjR2RINTFWaHYrMmJNWEpabmdNMTU0Sm0rcUJ4MGhOd1R0K1AyMGZ6ZlpueDh2TEM5c3RxZUxPVm9lNFhlZzhQS01TdFQ1ei9yRGQrZk1nTVB2MHd5bWVybEM5ajJ4L0tMYnRTQmtNa0hkTXZjWVY0cjhJNkFCV0tvNE50QmhTYk8wdUcreDUwbjdkOTFhb1R1ZmhrM3plajV1dEM0ZElGRmdFMkZXMmdvcldzSTBBT0huVXJadlgrYmJxYmFZUFMyT05XSW9wcW1hVVUrVXZPUzcwM2Q4WWtjSGFyaTdFREE1dVFKVG5xT1I1YkpieFk3U0hjWEVmMFZFeldpcGgrL0FKMmYvSzJUVUhoNjZZMnpqdnM4cHdKZURKRUQ0U080OEZHMEtOTjk3UzN5NEJMdz09PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjwveGVuYzpFbmNyeXB0ZWREYXRhPjwvc2FtbDI6RW5jcnlwdGVkQXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg==' } }, function(err, response, body) { if(err) return done(err); @@ -166,7 +166,35 @@ describe('samlp (functional tests)', function () { it('should return a 400', function(){ expect(r.statusCode) - .to.equal(400); + .to.equal(400); + }); + + it('should be recognized as an invalid xml', function(){ + var err = JSON.parse(bod); + expect(err.message) + .to.equal('SAMLResponse should be a valid xml'); + }); + }); + + describe('SAMLResponse with invalid CDATA', function() { + var r, bod; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: `${server.BASE_URL}/callback/samlp-with-invalid-xml`, + form: { SAMLResponse: Buffer.from('<doc><![CDATA[</doc>').toString('base64') } + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should return a 400', function(){ + expect(r.statusCode) + .to.equal(400); }); it('should be recognized as an invalid xml', function(){ @@ -182,7 +210,7 @@ describe('samlp (functional tests)', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback/samlp-with-utf8', + uri: `${server.BASE_URL}/callback/samlp-with-utf8`, form: { SAMLResponse: fs.readFileSync(path.join(__dirname, './samples/encoded/samlresponse_utf8.txt')).toString() } }, function(err, response, body) { if(err) return done(err); @@ -194,7 +222,7 @@ describe('samlp (functional tests)', function () { it('should be valid signature', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); it('should return a valid user', function(){ @@ -215,7 +243,7 @@ describe('samlp (functional tests)', function () { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback/samlp-with-ISO', + uri: `${server.BASE_URL}/callback/samlp-with-ISO`, form: { SAMLResponse: samlEncoded } }, function(err, response, body) { if(err) return done(err); @@ -227,7 +255,7 @@ describe('samlp (functional tests)', function () { it('should be valid signature', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); it('should return a valid user', function(){ @@ -248,7 +276,7 @@ describe('samlp (functional tests)', function () { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback/samlp-with-ISO-explicit', + uri: `${server.BASE_URL}/callback/samlp-with-ISO-explicit`, form: { SAMLResponse: samlEncoded } }, function(err, response, body) { if(err) return done(err); @@ -260,7 +288,7 @@ describe('samlp (functional tests)', function () { it('should be valid signature', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); it('should return a valid user', function(){ @@ -279,7 +307,7 @@ describe('samlp (functional tests)', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback/samlp-with-dsig-at-root', + uri: `${server.BASE_URL}/callback/samlp-with-dsig-at-root`, form: { SAMLResponse: fs.readFileSync(path.join(__dirname, './samples/encoded/samlresponse_signedassertion_dsprefix.txt')).toString() } }, function(err, response, body) { if(err) return done(err); @@ -291,7 +319,7 @@ describe('samlp (functional tests)', function () { it('should be valid signature', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); }); @@ -301,7 +329,7 @@ describe('samlp (functional tests)', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5051/callback', + uri: `${server.BASE_URL}/callback`, form: { SAMLResponse: 'foo' } }, function(err, response, body) { if(err) return done(err); @@ -313,7 +341,7 @@ describe('samlp (functional tests)', function () { it('should return a 400', function(){ expect(r.statusCode) - .to.equal(400); + .to.equal(400); }); }); @@ -325,7 +353,7 @@ describe('samlp (functional tests)', function () { request.get({ jar: request.jar(), followRedirect: false, - uri: 'http://localhost:5051/login' + uri: `${server.BASE_URL}/login` }, function (err, resp, b){ if(err) return done(err); r = resp; @@ -336,38 +364,38 @@ describe('samlp (functional tests)', function () { it('should redirect to idp', function(){ expect(r.statusCode) - .to.equal(302); + .to.equal(302); }); it('should have SAMLRequest querystring', function(done){ expect(r.headers.location.split('?')[0]) - .to.equal(server.identityProviderUrl); + .to.equal(server.identityProviderUrl); var querystring = qs.parse(r.headers.location.split('?')[1]); expect(querystring).to.have.property('SAMLRequest'); var SAMLRequest = querystring.SAMLRequest; zlib.inflateRaw(new Buffer(SAMLRequest, 'base64'), function (err, buffer) { if (err) return done(err); var request = buffer.toString(); - var doc = new xmldom.DOMParser().parseFromString(request); + var doc = new xmldom.DOMParser().parseFromString(request, 'text/xml'); expect(doc.documentElement.getAttribute('ProtocolBinding')) - .to.equal('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); + .to.equal('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); expect(doc.documentElement.getAttribute('Version')) - .to.equal('2.0'); + .to.equal('2.0'); expect(doc.documentElement.getElementsByTagName('saml:Issuer')[0] - .getAttribute('xmlns:saml')) - .to.equal('urn:oasis:names:tc:SAML:2.0:assertion'); + .getAttribute('xmlns:saml')) + .to.equal('urn:oasis:names:tc:SAML:2.0:assertion'); done(); }); }); it('should have RelayState querystring', function(){ expect(r.headers.location.split('?')[0]) - .to.equal(server.identityProviderUrl); + .to.equal(server.identityProviderUrl); var querystring = qs.parse(r.headers.location.split('?')[1]); expect(querystring).to.have.property('RelayState'); expect(querystring.RelayState).to.equal(server.relayState); @@ -381,7 +409,7 @@ describe('samlp (functional tests)', function () { request.get({ jar: request.jar(), followRedirect: false, - uri: 'http://localhost:5051/login-http-post' + uri: `${server.BASE_URL}/login-http-post` }, function (err, resp, b){ if (err) return done(err); r = resp; @@ -394,23 +422,23 @@ describe('samlp (functional tests)', function () { it('should post to idp', function(){ expect(r.statusCode).to.equal(200); expect(r.headers['content-type']).to.contains('text/html'); - expect($('form').attr('action')).to.equal('http://localhost:5051/samlp'); + expect($('form').attr('action')).to.equal(`${server.BASE_URL}/samlp`); }); it('should have SAMLRequest input', function (done) { var SAMLRequest = $('form input[name="SAMLRequest"]').val(); expect(SAMLRequest).to.be.ok; - var doc = new xmldom.DOMParser().parseFromString(new Buffer(SAMLRequest, 'base64').toString()); + var doc = new xmldom.DOMParser().parseFromString(new Buffer(SAMLRequest, 'base64').toString(), 'text/xml'); expect(doc.documentElement.getAttribute('ProtocolBinding')) - .to.equal('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); + .to.equal('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); expect(doc.documentElement.getAttribute('Version')) - .to.equal('2.0'); + .to.equal('2.0'); expect(doc.documentElement.getElementsByTagName('saml:Issuer')[0] - .getAttribute('xmlns:saml')) - .to.equal('urn:oasis:names:tc:SAML:2.0:assertion'); + .getAttribute('xmlns:saml')) + .to.equal('urn:oasis:names:tc:SAML:2.0:assertion'); done(); }); @@ -430,7 +458,7 @@ describe('samlp (functional tests)', function () { request.get({ jar: request.jar(), followRedirect: false, - uri: 'http://localhost:5051/login-custom-request-template' + uri: `${server.BASE_URL}/login-custom-request-template` }, function (err, resp, b){ if(err) return done(err); r = resp; @@ -441,32 +469,32 @@ describe('samlp (functional tests)', function () { it('should redirect to idp', function(){ expect(r.statusCode) - .to.equal(302); + .to.equal(302); }); it('should have SAMLRequest querystring', function(done){ expect(r.headers.location.split('?')[0]) - .to.equal(server.identityProviderUrl); + .to.equal(server.identityProviderUrl); var querystring = qs.parse(r.headers.location.split('?')[1]); expect(querystring).to.have.property('SAMLRequest'); var SAMLRequest = querystring.SAMLRequest; zlib.inflateRaw(new Buffer(SAMLRequest, 'base64'), function (err, buffer) { if (err) return done(err); var request = buffer.toString(); - var doc = new xmldom.DOMParser().parseFromString(request); + var doc = new xmldom.DOMParser().parseFromString(request, 'text/xml'); expect(doc.documentElement.getAttribute('Protocol')) - .to.equal('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); + .to.equal('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); expect(doc.documentElement.getAttribute('Version')) - .to.equal('3.0'); + .to.equal('3.0'); expect(doc.documentElement.getAttribute('Foo')) - .to.equal('123'); + .to.equal('123'); expect(doc.documentElement.getAttribute('Issuertico')) - .to.equal('https://auth0-dev-ed.my.salesforce.com'); + .to.equal('https://auth0-dev-ed.my.salesforce.com'); done(); }); @@ -482,7 +510,7 @@ describe('samlp (functional tests)', function () { request.get({ jar: request.jar(), followRedirect: false, - uri: 'http://localhost:5051/login-idp-with-querystring' + uri: `${server.BASE_URL}/login-idp-with-querystring` }, function (err, resp, b){ if(err) return done(err); r = resp; @@ -493,12 +521,12 @@ describe('samlp (functional tests)', function () { it('should redirect to idp', function(){ expect(r.statusCode) - .to.equal(302); + .to.equal(302); }); it('should have SAMLRequest and foo in querystring', function(){ expect(r.headers.location.split('?')[0]) - .to.equal(server.identityProviderUrl); + .to.equal(server.identityProviderUrl); var querystring = qs.parse(r.headers.location.split('?')[1]); expect(querystring).to.have.property('SAMLRequest'); expect(querystring).to.have.property('foo'); @@ -508,14 +536,16 @@ describe('samlp (functional tests)', function () { describe('samlp with signed request', function () { describe('POST binding', function () { - var r, bod, $; + let r, bod, $; before(function (done) { request.get({ jar: request.jar(), - uri: 'http://localhost:5051/login-signed-request-post' + uri: `${server.BASE_URL}/login-signed-request-post` }, function (err, resp, b){ - if(err) return callback(err); + if (err) { + return done(err); + } r = resp; bod = b; $ = cheerio.load(bod); @@ -525,7 +555,7 @@ describe('samlp (functional tests)', function () { it('should return 200 with form element', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); it('should have signed SAMLRequest with valid signature', function(done){ @@ -534,15 +564,15 @@ describe('samlp (functional tests)', function () { var signingCert = fs.readFileSync(__dirname + '/test-auth0.pem'); expect(helpers.isValidSignature(signedRequest, signingCert)) - .to.equal(true); + .to.equal(true); done(); }); it('should show issuer before signature', function(done){ var signedSAMLRequest = $('form input[name="SAMLRequest"]').val(); var signedRequest = new Buffer(signedSAMLRequest, 'base64').toString(); - var doc = new xmldom.DOMParser().parseFromString(signedRequest); + var doc = new xmldom.DOMParser().parseFromString(signedRequest, 'text/xml'); // First child has to be the issuer expect(doc.documentElement.childNodes[0].nodeName).to.equal('saml:Issuer'); @@ -559,7 +589,7 @@ describe('samlp (functional tests)', function () { request.get({ jar: request.jar(), followRedirect: false, - uri: 'http://localhost:5051/login-signed-request-without-deflate' + uri: `${server.BASE_URL}/login-signed-request-without-deflate` }, function (err, resp, b){ if(err) return callback(err); r = resp; @@ -570,12 +600,12 @@ describe('samlp (functional tests)', function () { it('should redirect to idp', function(){ expect(r.statusCode) - .to.equal(302); + .to.equal(302); }); it('should have signed SAMLRequest with valid signature', function(done){ expect(r.headers.location.split('?')[0]) - .to.equal(server.identityProviderUrl); + .to.equal(server.identityProviderUrl); var querystring = qs.parse(r.headers.location.split('?')[1]); expect(querystring).to.have.property('SAMLRequest'); expect(querystring.RelayState).to.equal('somestate'); @@ -585,7 +615,7 @@ describe('samlp (functional tests)', function () { var signingCert = fs.readFileSync(__dirname + '/test-auth0.pem'); expect(helpers.isValidSignature(signedRequest, signingCert)) - .to.equal(true); + .to.equal(true); done(); }); }); @@ -597,9 +627,9 @@ describe('samlp (functional tests)', function () { request.get({ jar: request.jar(), followRedirect: false, - uri: 'http://localhost:5051/login-signed-request-with-deflate' + uri: `${server.BASE_URL}/login-signed-request-with-deflate` }, function (err, resp, b){ - if(err) return callback(err); + if(err) return done(err); r = resp; bod = b; done(); @@ -608,12 +638,12 @@ describe('samlp (functional tests)', function () { it('should redirect to idp', function(){ expect(r.statusCode) - .to.equal(302); + .to.equal(302); }); it('should have signed SAMLRequest with valid signature', function(done){ expect(r.headers.location.split('?')[0]) - .to.equal(server.identityProviderUrl); + .to.equal(server.identityProviderUrl); var querystring = qs.parse(r.headers.location.split('?')[1]); expect(querystring).to.have.property('SAMLRequest'); expect(querystring).to.have.property('Signature'); @@ -646,7 +676,7 @@ function doSamlpFlow(samlRequestUrl, callbackEndpoint, callback) { }, function (err, response, b){ if(err) return callback(err); expect(response.statusCode) - .to.equal(200); + .to.equal(200); var $ = cheerio.load(b); var SAMLResponse = $('input[name="SAMLResponse"]').attr('value'); @@ -661,4 +691,4 @@ function doSamlpFlow(samlRequestUrl, callbackEndpoint, callback) { callback(null, { response: response, body: body }); }); }); -} +} \ No newline at end of file
test/samlp.tests.js+178 −75 modifiedtest/state/samlp.state.custom.tests.js+184 −184 modified@@ -8,155 +8,155 @@ chai.use(passport); describe('samlp - using custom session state store', function() { var SAMLResponse = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfOGU4ZGM1ZjY5YTk4Y2M0YzFmZjM0MjdlNWNlMzQ2MDZmZDY3MmY5MWU2IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1Ij4NCiAgPHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj4NCiAgPHNhbWxwOlN0YXR1cz4NCiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+DQogIDwvc2FtbHA6U3RhdHVzPg0KICA8c2FtbDpBc3NlcnRpb24geG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiBJRD0iX2Q3MWEzYThlOWZjYzQ1YzllOWQyNDhlZjcwNDkzOTNmYzhmMDRlNWY3NSIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPg0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCI+X2NlM2QyOTQ4YjRjZjIwMTQ2ZGVlMGEwYjNkZDZmNjliNmNmODZmNjJkNzwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiIFJlY2lwaWVudD0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL2luZGV4LnBocD9hY3MiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDctMTdUMDE6MDE6MThaIiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3RAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcnM8L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmV4YW1wbGVyb2xlMTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+'; - + describe('that accepts meta argument', function() { function CustomStore() {} CustomStore.prototype.store = function(req, meta, cb) { if (req.url === '/error') { return cb(new Error('something went wrong storing state')); } if (req.url === '/exception') { throw new Error('something went horribly wrong storing state'); } - + if (req.url !== '/me') { return cb(new Error('incorrect req argument')); } if (meta.identityProviderUrl !== 'http://www.example.com/samlp') { return cb(new Error('incorrect meta.identityProviderUrl argument')); } - + req.customStoreStoreCalled = req.customStoreStoreCalled ? req.customStoreStoreCalled++ : 1; return cb(null, 'foos7473'); }; - + CustomStore.prototype.verify = function(req, state, meta, cb) { if (req.url === '/error') { return cb(new Error('something went wrong verifying state')); } if (req.url === '/exception') { throw new Error('something went horribly wrong verifying state'); } - + if (state !== 'foos7473') { return cb(new Error('incorrect state argument')); } if (meta.identityProviderUrl !== 'http://www.example.com/samlp') { return cb(new Error('incorrect meta.identityProviderUrl argument')); } - + req.customStoreVerifyCalled = req.customStoreVerifyCalled ? req.customStoreVerifyCalled++ : 1; return cb(null, true); }; - + describe('issuing authorization request', function() { var strategy = new Strategy({ - protocol: 'samlp', - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: 'http://www.example.com/samlp', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - store: new CustomStore() - }, - function() {}); - + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function() {}); + describe('that redirects to service provider', function() { var request, url; - + before(function (done) { chai.passport.use(strategy) - .redirect(function(u) { - url = u; - done(); - }) - .req(function(req) { - request = req; - req.url = '/me'; - }) - .authenticate({}); + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.url = '/me'; + }) + .authenticate({}); }); - + it('should be redirected', function() { expect(url).to.have.string('http://www.example.com/samlp?SAMLRequest='); expect(url).to.have.string('&RelayState=foos7473'); }); - + it('should serialize state using custom store', function() { expect(request.customStoreStoreCalled).to.equal(1); }); }); - + describe('that errors due to custom store supplying error', function() { var request, err; - + before(function (done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - req.url = '/error'; - }) - .authenticate({}); + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + req.url = '/error'; + }) + .authenticate({}); }); - + it('should error', function() { expect(err).to.be.an.instanceof(Error); expect(err.message).to.equal('something went wrong storing state'); }); }); - + describe('that errors due to custom store throwing error', function() { var request, err; - + before(function (done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - req.url = '/exception'; - }) - .authenticate({}); + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + req.url = '/exception'; + }) + .authenticate({}); }); - + it('should error', function() { expect(err).to.be.an.instanceof(Error); expect(err.message).to.equal('something went horribly wrong storing state'); }); }); }); - + describe('processing response to authorization request', function() { var strategy = new Strategy({ - protocol: 'samlp', - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: 'http://www.example.com/samlp', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - store: new CustomStore() - }, - function (profile, done) { - return done(null, profile, { message: 'Hello' }); - }); + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); - strategy._samlp.validateSamlResponse = function(token, done) { - expect(token).to.be.an('object'); + strategy._samlp.validateSamlResponse = function(token, _options, done) { + expect(token).to.be.a('string'); done(null, { id: '1234' }); }; - + describe('that was approved', function() { var request, user, info; before(function (done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.url = '/login'; - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'foos7473'; - req.method = 'POST'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); }); it('should supply user', function() { @@ -168,58 +168,58 @@ describe('samlp - using custom session state store', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Hello'); }); - + it('should verify state using custom store', function() { expect(request.customStoreVerifyCalled).to.equal(1); }); }); - + describe('that errors due to custom store supplying error', function() { var request, err; before(function (done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - - req.url = '/error'; - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'foos7473'; - req.method = 'POST'; - }) - .authenticate({}); + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/error'; + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'foos7473'; + req.method = 'POST'; + }) + .authenticate({}); }); it('should error', function() { expect(err).to.be.an.instanceof(Error); expect(err.message).to.equal('something went wrong verifying state'); }); }); - + describe('that errors due to custom store throwing error', function() { var request, err; before(function (done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - - req.url = '/exception'; - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'foos7473'; - req.method = 'POST'; - }) - .authenticate({}); + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/exception'; + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'foos7473'; + req.method = 'POST'; + }) + .authenticate({}); }); it('should error', function() { @@ -229,57 +229,57 @@ describe('samlp - using custom session state store', function() { }); }); }); - + describe('that accepts meta argument and supplies state', function() { function CustomStore() {} - + CustomStore.prototype.verify = function(req, state, meta, cb) { req.customStoreVerifyCalled = req.customStoreVerifyCalled ? req.customStoreVerifyCalled++ : 1; return cb(null, true, { returnTo: 'http://www.example.com/' }); }; - + describe('processing response to authorization request', function() { - + describe('that was approved without info', function() { var strategy = new Strategy({ - protocol: 'samlp', - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: 'http://www.example.com/samlp', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - store: new CustomStore() - }, - function (profile, done) { - return done(null, profile); - }); + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile); + }); - strategy._samlp.validateSamlResponse = function(token, done) { - expect(token).to.be.an('object'); + strategy._samlp.validateSamlResponse = function(token, _options, done) { + expect(token).to.be.a('string'); done(null, { id: '1234' }); }; - + var request, user, info; before(function (done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.url = '/login'; - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'foos7473'; - req.method = 'POST'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); }); it('should supply user', function() { @@ -293,52 +293,52 @@ describe('samlp - using custom session state store', function() { expect(info.state).to.be.an('object'); expect(info.state.returnTo).to.equal('http://www.example.com/'); }); - + it('should verify state using custom store', function() { expect(request.customStoreVerifyCalled).to.equal(1); }); }); - + describe('that was approved with info', function() { var strategy = new Strategy({ - protocol: 'samlp', - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: 'http://www.example.com/samlp', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - store: new CustomStore() - }, - function (profile, done) { - return done(null, profile, { message: 'Hello' }); - }); + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); - strategy._samlp.validateSamlResponse = function(token, done) { - expect(token).to.be.an('object'); + strategy._samlp.validateSamlResponse = function(token, _options, done) { + expect(token).to.be.a('string'); done(null, { id: '1234' }); }; var request, user, info; before(function (done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.url = '/login'; - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'foos7473'; - req.method = 'POST'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); }); it('should supply user', function() { @@ -353,11 +353,11 @@ describe('samlp - using custom session state store', function() { expect(info.state).to.be.an('object'); expect(info.state.returnTo).to.equal('http://www.example.com/'); }); - + it('should verify state using custom store', function() { expect(request.customStoreVerifyCalled).to.equal(1); }); }); }); }); -}); +}); \ No newline at end of file
test/state/samlp.state.session.tests.js+251 −251 modified@@ -9,9 +9,9 @@ chai.use(passport); describe('samlp - using default session state store', function() { var SAMLResponse = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfOGU4ZGM1ZjY5YTk4Y2M0YzFmZjM0MjdlNWNlMzQ2MDZmZDY3MmY5MWU2IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1Ij4NCiAgPHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj4NCiAgPHNhbWxwOlN0YXR1cz4NCiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+DQogIDwvc2FtbHA6U3RhdHVzPg0KICA8c2FtbDpBc3NlcnRpb24geG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiBJRD0iX2Q3MWEzYThlOWZjYzQ1YzllOWQyNDhlZjcwNDkzOTNmYzhmMDRlNWY3NSIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPg0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCI+X2NlM2QyOTQ4YjRjZjIwMTQ2ZGVlMGEwYjNkZDZmNjliNmNmODZmNjJkNzwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiIFJlY2lwaWVudD0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL2luZGV4LnBocD9hY3MiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDctMTdUMDE6MDE6MThaIiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3RAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcnM8L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmV4YW1wbGVyb2xlMTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+'; - + describe('without session key option', function() { - + describe('issuing authorization request', function() { var strategy = new Strategy({ protocol: 'samlp', @@ -21,421 +21,421 @@ describe('samlp - using default session state store', function() { thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], state: true }, function () {}); - + describe('that redirects to service provider', function() { var request, url; - + before(function(done) { chai.passport.use(strategy) - .redirect(function(u) { - url = u; - done(); - }) - .req(function(req) { - request = req; - req.session = {}; - }) - .authenticate({}); - }); - + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + }) + .authenticate({}); + }); + it('should be redirected', function() { var u = uri.parse(url, true); expect(u.query.RelayState).to.have.length(24); }); - + it('should save state in session', function() { var u = uri.parse(url, true); expect(request.session['samlp:www.example.com'].state).to.have.length(24); expect(request.session['samlp:www.example.com'].state).to.equal(u.query.RelayState); }); }); - + describe('that redirects to service provider with other data in session', function() { var request, url; - + before(function(done) { chai.passport.use(strategy) - .redirect(function(u) { - url = u; - done(); - }) - .req(function(req) { - request = req; - req.session = {}; - req.session['samlp:www.example.com'] = {}; - req.session['samlp:www.example.com'].foo = 'bar'; - }) - .authenticate({}); - }); - + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + req.session['samlp:www.example.com'] = {}; + req.session['samlp:www.example.com'].foo = 'bar'; + }) + .authenticate({}); + }); + it('should be redirected', function() { var u = uri.parse(url, true); expect(u.query.RelayState).to.have.length(24); }); - + it('should save state in session', function() { var u = uri.parse(url, true); - + expect(request.session['samlp:www.example.com'].state).to.have.length(24); expect(request.session['samlp:www.example.com'].state).to.equal(u.query.RelayState); }); - + it('should preserve other data in session', function() { expect(request.session['samlp:www.example.com'].foo).to.equal('bar'); }); }); - + describe('that errors due to lack of session support in app', function() { var request, err; - + before(function(done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - }) - .authenticate({}); - }); - + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + }) + .authenticate({}); + }); + it('should error', function() { expect(err).to.be.an.instanceof(Error); expect(err.message).to.equal('Authentication requires session support when using state. Did you forget to use express-session middleware?'); }); }); }); - + describe('processing response to authorization request', function() { var strategy = new Strategy({ - protocol: 'samlp', - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: 'http://www.example.com/samlp', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - state: true - }, - function (profile, done) { - return done(null, profile, { message: 'Hello' }); - }); + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + state: true + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); - strategy._samlp.validateSamlResponse = function(token, done) { - expect(token).to.be.an('object'); + strategy._samlp.validateSamlResponse = function(token, options, done) { + expect(token).to.be.a('string'); done(null, { id: '1234' }); }; - + describe('that was approved', function() { var request, user, info; - + before(function (done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.method = 'POST'; - req.session = {}; - req.session['samlp:www.example.com'] = {}; - req.session['samlp:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); - }); - + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['samlp:www.example.com'] = {}; + req.session['samlp:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + it('should supply user', function() { expect(user).to.be.an('object'); expect(user.id).to.equal('1234'); }); - + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Hello'); }); - + it('should remove state from session', function() { expect(request.session['samlp:www.example.com']).to.be.undefined; }); }); - + describe('that was approved with other data in the session', function() { var request, user, info; - + before(function(done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.method = 'POST'; - req.session = {}; - req.session['samlp:www.example.com'] = {}; - req.session['samlp:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.session['samlp:www.example.com'].foo = 'bar'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); - }); - + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['samlp:www.example.com'] = {}; + req.session['samlp:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.session['samlp:www.example.com'].foo = 'bar'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + it('should supply user', function() { expect(user).to.be.an('object'); expect(user.id).to.equal('1234'); }); - + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Hello'); }); - + it('should preserve other data from session', function() { expect(request.session['samlp:www.example.com'].state).to.be.undefined; expect(request.session['samlp:www.example.com'].foo).to.equal('bar'); }); }); - + describe('that fails due to state being invalid', function() { var request, info, status; - + before(function (done) { chai.passport.use(strategy) - .fail(function(i, s) { - info = i; - status = s; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; - req.method = 'POST'; - req.session = {}; - req.session['samlp:www.example.com'] = {}; - req.session['samlp:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; - }) - .authenticate({}); - }); - + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; + req.method = 'POST'; + req.session = {}; + req.session['samlp:www.example.com'] = {}; + req.session['samlp:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + }) + .authenticate({}); + }); + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Invalid authorization request state.'); }); - + it('should supply status', function() { expect(status).to.equal(403); }); - + it('should remove state from session', function() { expect(request.session['samlp:www.example.com']).to.be.undefined; }); }); - + describe('that fails due to provider-specific state not found in session', function() { var request, info, status; - + before(function(done) { chai.passport.use(strategy) - .fail(function(i, s) { - info = i; - status = s; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.method = 'POST'; - req.session = {}; - }) - .authenticate({}); - }); - + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + }) + .authenticate({}); + }); + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Unable to verify authorization request state.'); }); - + it('should supply status', function() { expect(status).to.equal(403); }); }); - + describe('that fails due to provider-specific state lacking state value', function() { var request, info, status; - + before(function(done) { chai.passport.use(strategy) - .fail(function(i, s) { - info = i; - status = s; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.method = 'POST'; - req.session = {}; - req.session['samlp:www.example.com'] = {}; - }) - .authenticate({}); - }); - + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['samlp:www.example.com'] = {}; + }) + .authenticate({}); + }); + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Unable to verify authorization request state.'); }); - + it('should supply status', function() { expect(status).to.equal(403); }); }); - + describe('that errors due to lack of session support in app', function() { var request, err; - + before(function (done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.method = 'POST'; - }) - .authenticate({}); - }); - + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + }) + .authenticate({}); + }); + it('should error', function() { expect(err).to.be.an.instanceof(Error); expect(err.message).to.equal('Authentication requires session support when using state. Did you forget to use express-session middleware?'); }); }); }); }); - + describe('with session key option', function() { var strategy = new Strategy({ - protocol: 'samlp', - path: '/callback', - realm: 'https://auth0-dev-ed.my.salesforce.com', - identityProviderUrl: 'http://www.example.com/samlp', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - state: true, - sessionKey: 'samlp:example' - }, - function (profile, done) { - return done(null, profile, { message: 'Hello' }); - }); + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + state: true, + sessionKey: 'samlp:example' + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); - strategy._samlp.validateSamlResponse = function(token, done) { - expect(token).to.be.an('object'); + strategy._samlp.validateSamlResponse = function(token, _options, done) { + expect(token).to.be.a('string'); done(null, { id: '1234' }); }; - + describe('issuing authorization request', function() { - + describe('that redirects to service provider', function() { var request, url; - + before(function (done) { chai.passport.use(strategy) - .redirect(function(u) { - url = u; - done(); - }) - .req(function(req) { - request = req; - req.session = {}; - }) - .authenticate({}); - }); - + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + }) + .authenticate({}); + }); + it('should be redirected', function() { var u = uri.parse(url, true); expect(u.query.RelayState).to.have.length(24); }); - + it('should save state in session', function() { var u = uri.parse(url, true); - + expect(request.session['samlp:example'].state).to.have.length(24); expect(request.session['samlp:example'].state).to.equal(u.query.RelayState); }); }); }); - + describe('processing response to authorization request', function() { - + describe('that was approved', function() { var request, user, info; - + before(function (done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.SAMLResponse = SAMLResponse; - req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.method = 'POST'; - req.session = {}; - req.session['samlp:example'] = {}; - req.session['samlp:example']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); - }); - + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['samlp:example'] = {}; + req.session['samlp:example']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + it('should supply user', function() { expect(user).to.be.an('object'); expect(user.id).to.equal('1234'); }); - + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Hello'); }); - + it('should remove state from session', function() { expect(request.session['samlp:example']).to.be.undefined; }); }); }); }); -}); +}); \ No newline at end of file
test/state/wsfed.state.custom.tests.js+191 −187 modified@@ -1,162 +1,166 @@ -var chai = require('chai'); -var expect = require('chai').expect; -var passport = require('chai-passport-strategy'); -var Strategy = require('../../lib/passport-wsfed-saml2').Strategy; +const chai = require('chai'); +const expect = require('chai').expect; +const passport = require('chai-passport-strategy'); +const Strategy = require('../../lib/passport-wsfed-saml2').Strategy; + +const xmldom = require('@xmldom/xmldom'); +const domParser = new xmldom.DOMParser(); + chai.use(passport); describe('wsfed - using custom session state store', function() { - + describe('that accepts meta argument', function() { function CustomStore() {} CustomStore.prototype.store = function(req, meta, cb) { if (req.url === '/error') { return cb(new Error('something went wrong storing state')); } if (req.url === '/exception') { throw new Error('something went horribly wrong storing state'); } - + if (req.url !== '/me') { return cb(new Error('incorrect req argument')); } if (meta.identityProviderUrl !== 'http://www.example.com/login') { return cb(new Error('incorrect meta.identityProviderUrl argument')); } - + req.customStoreStoreCalled = req.customStoreStoreCalled ? req.customStoreStoreCalled++ : 1; return cb(null, 'foos7473'); }; - + CustomStore.prototype.verify = function(req, state, meta, cb) { if (req.url === '/error') { return cb(new Error('something went wrong verifying state')); } if (req.url === '/exception') { throw new Error('something went horribly wrong verifying state'); } - + if (state !== 'foos7473') { return cb(new Error('incorrect state argument')); } if (meta.identityProviderUrl !== 'http://www.example.com/login') { return cb(new Error('incorrect meta.identityProviderUrl argument')); } - + req.customStoreVerifyCalled = req.customStoreVerifyCalled ? req.customStoreVerifyCalled++ : 1; return cb(null, true); }; - + describe('issuing authorization request', function() { var strategy = new Strategy({ - path: '/callback', - realm: 'urn:fixture-test', - identityProviderUrl: 'http://www.example.com/login', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - store: new CustomStore() - }, - function() {}); - + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function() {}); + describe('that redirects to service provider', function() { var request, url; - + before(function (done) { chai.passport.use(strategy) - .redirect(function(u) { - url = u; - done(); - }) - .req(function(req) { - request = req; - req.url = '/me'; - }) - .authenticate({}); + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.url = '/me'; + }) + .authenticate({}); }); - + it('should be redirected', function() { expect(url).to.equal('http://www.example.com/login?wctx=foos7473&wtrealm=urn%3Afixture-test&wa=wsignin1.0&whr='); }); - + it('should serialize state using custom store', function() { expect(request.customStoreStoreCalled).to.equal(1); }); }); - + describe('that errors due to custom store supplying error', function() { var request, err; - + before(function (done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - req.url = '/error'; - }) - .authenticate({}); + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + req.url = '/error'; + }) + .authenticate({}); }); - + it('should error', function() { expect(err).to.be.an.instanceof(Error); expect(err.message).to.equal('something went wrong storing state'); }); }); - + describe('that errors due to custom store throwing error', function() { var request, err; - + before(function (done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - req.url = '/exception'; - }) - .authenticate({}); + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + req.url = '/exception'; + }) + .authenticate({}); }); - + it('should error', function() { expect(err).to.be.an.instanceof(Error); expect(err.message).to.equal('something went horribly wrong storing state'); }); }); }); - + describe('processing response to authorization request', function() { var strategy = new Strategy({ - path: '/callback', - realm: 'urn:fixture-test', - identityProviderUrl: 'http://www.example.com/login', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - store: new CustomStore() - }, - function (profile, done) { - return done(null, profile, { message: 'Hello' }); - }); + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); strategy._wsfed.extractToken = function(req) { expect(req).to.be.an('object'); - return '<trust:RequestedSecurityToken>...</trust:RequestedSecurityToken>'; + return domParser.parseFromString('<trust:RequestedSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestedSecurityToken>', 'text/xml'); }; - strategy._saml.validateSamlAssertion = function(token, done) { - expect(token).to.equal('<trust:RequestedSecurityToken>...</trust:RequestedSecurityToken>'); + strategy._saml.validateSamlAssertion = function(wResult, _options, done) { + expect(wResult).to.equal('<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'); done(null, { id: '1234' }); }; - + describe('that was approved', function() { var request, user, info; before(function (done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.url = '/login'; - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'foos7473'; - req.method = 'POST'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); }); it('should supply user', function() { @@ -168,58 +172,58 @@ describe('wsfed - using custom session state store', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Hello'); }); - + it('should verify state using custom store', function() { expect(request.customStoreVerifyCalled).to.equal(1); }); }); - + describe('that errors due to custom store supplying error', function() { var request, err; before(function (done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - - req.url = '/error'; - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'foos7473'; - req.method = 'POST'; - }) - .authenticate({}); + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/error'; + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'foos7473'; + req.method = 'POST'; + }) + .authenticate({}); }); it('should error', function() { expect(err).to.be.an.instanceof(Error); expect(err.message).to.equal('something went wrong verifying state'); }); }); - + describe('that errors due to custom store throwing error', function() { var request, err; before(function (done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - - req.url = '/exception'; - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'foos7473'; - req.method = 'POST'; - }) - .authenticate({}); + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/exception'; + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'foos7473'; + req.method = 'POST'; + }) + .authenticate({}); }); it('should error', function() { @@ -229,61 +233,61 @@ describe('wsfed - using custom session state store', function() { }); }); }); - + describe('that accepts meta argument and supplies state', function() { function CustomStore() {} - + CustomStore.prototype.verify = function(req, state, meta, cb) { req.customStoreVerifyCalled = req.customStoreVerifyCalled ? req.customStoreVerifyCalled++ : 1; return cb(null, true, { returnTo: 'http://www.example.com/' }); }; - + describe('processing response to authorization request', function() { - + describe('that was approved without info', function() { var strategy = new Strategy({ - path: '/callback', - realm: 'urn:fixture-test', - identityProviderUrl: 'http://www.example.com/login', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - store: new CustomStore() - }, - function (profile, done) { - return done(null, profile); - }); + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile); + }); strategy._wsfed.extractToken = function(req) { expect(req).to.be.an('object'); - return '<trust:RequestedSecurityToken>...</trust:RequestedSecurityToken>'; + return domParser.parseFromString('<trust:RequestedSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestedSecurityToken>', 'text/xml'); }; - strategy._saml.validateSamlAssertion = function(token, done) { - expect(token).to.equal('<trust:RequestedSecurityToken>...</trust:RequestedSecurityToken>'); + strategy._saml.validateSamlAssertion = function(token, _options, done) { + expect(token).to.equal('<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'); done(null, { id: '1234' }); }; - + var request, user, info; before(function (done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.url = '/login'; - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'foos7473'; - req.method = 'POST'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); }); it('should supply user', function() { @@ -297,56 +301,56 @@ describe('wsfed - using custom session state store', function() { expect(info.state).to.be.an('object'); expect(info.state.returnTo).to.equal('http://www.example.com/'); }); - + it('should verify state using custom store', function() { expect(request.customStoreVerifyCalled).to.equal(1); }); }); - + describe('that was approved with info', function() { var strategy = new Strategy({ - path: '/callback', - realm: 'urn:fixture-test', - identityProviderUrl: 'http://www.example.com/login', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - store: new CustomStore() - }, - function (profile, done) { - return done(null, profile, { message: 'Hello' }); - }); + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); strategy._wsfed.extractToken = function(req) { expect(req).to.be.an('object'); - return '<trust:RequestedSecurityToken>...</trust:RequestedSecurityToken>'; + return domParser.parseFromString('<trust:RequestedSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestedSecurityToken>', 'text/xml'); }; - strategy._saml.validateSamlAssertion = function(token, done) { - expect(token).to.equal('<trust:RequestedSecurityToken>...</trust:RequestedSecurityToken>'); + strategy._saml.validateSamlAssertion = function(token, _options, done) { + expect(token).to.equal('<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'); done(null, { id: '1234' }); }; var request, user, info; before(function (done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.url = '/login'; - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'foos7473'; - req.method = 'POST'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); }); it('should supply user', function() { @@ -361,11 +365,11 @@ describe('wsfed - using custom session state store', function() { expect(info.state).to.be.an('object'); expect(info.state.returnTo).to.equal('http://www.example.com/'); }); - + it('should verify state using custom store', function() { expect(request.customStoreVerifyCalled).to.equal(1); }); }); }); }); -}); +}); \ No newline at end of file
test/state/wsfed.state.session.tests.js+260 −256 modified@@ -1,15 +1,19 @@ -var chai = require('chai'); -var uri = require('url'); -var expect = require('chai').expect; -var passport = require('chai-passport-strategy'); -var Strategy = require('../../lib/passport-wsfed-saml2').Strategy; +const chai = require('chai'); +const uri = require('url'); +const expect = require('chai').expect; +const passport = require('chai-passport-strategy'); +const Strategy = require('../../lib/passport-wsfed-saml2').Strategy; + +const xmldom = require('@xmldom/xmldom'); +const domParser = new xmldom.DOMParser(); + chai.use(passport); describe('wsfed - using default session state store', function() { - + describe('without session key option', function() { - + describe('issuing authorization request', function() { var strategy = new Strategy({ path: '/callback', @@ -18,429 +22,429 @@ describe('wsfed - using default session state store', function() { thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], state: true }, function () {}); - + describe('that redirects to service provider', function() { var request, url; - + before(function(done) { chai.passport.use(strategy) - .redirect(function(u) { - url = u; - done(); - }) - .req(function(req) { - request = req; - req.session = {}; - }) - .authenticate({}); - }); - + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + }) + .authenticate({}); + }); + it('should be redirected', function() { var u = uri.parse(url, true); expect(u.query.wctx).to.have.length(24); }); - + it('should save state in session', function() { var u = uri.parse(url, true); expect(request.session['wsfed:www.example.com'].state).to.have.length(24); expect(request.session['wsfed:www.example.com'].state).to.equal(u.query.wctx); }); }); - + describe('that redirects to service provider with other data in session', function() { var request, url; - + before(function(done) { chai.passport.use(strategy) - .redirect(function(u) { - url = u; - done(); - }) - .req(function(req) { - request = req; - req.session = {}; - req.session['wsfed:www.example.com'] = {}; - req.session['wsfed:www.example.com'].foo = 'bar'; - }) - .authenticate({}); - }); - + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + req.session['wsfed:www.example.com'] = {}; + req.session['wsfed:www.example.com'].foo = 'bar'; + }) + .authenticate({}); + }); + it('should be redirected', function() { var u = uri.parse(url, true); expect(u.query.wctx).to.have.length(24); }); - + it('should save state in session', function() { var u = uri.parse(url, true); - + expect(request.session['wsfed:www.example.com'].state).to.have.length(24); expect(request.session['wsfed:www.example.com'].state).to.equal(u.query.wctx); }); - + it('should preserve other data in session', function() { expect(request.session['wsfed:www.example.com'].foo).to.equal('bar'); }); }); - + describe('that errors due to lack of session support in app', function() { var request, err; - + before(function(done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - }) - .authenticate({}); - }); - + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + }) + .authenticate({}); + }); + it('should error', function() { expect(err).to.be.an.instanceof(Error); expect(err.message).to.equal('Authentication requires session support when using state. Did you forget to use express-session middleware?'); }); }); }); - + describe('processing response to authorization request', function() { var strategy = new Strategy({ - path: '/callback', - realm: 'urn:fixture-test', - identityProviderUrl: 'http://www.example.com/login', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - state: true - }, - function (profile, done) { - return done(null, profile, { message: 'Hello' }); - }); + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + state: true + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); strategy._wsfed.extractToken = function(req) { expect(req).to.be.an('object'); - return '<trust:RequestedSecurityToken>...</trust:RequestedSecurityToken>'; + return domParser.parseFromString('<trust:RequestedSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestedSecurityToken>', 'text/xml'); }; - strategy._saml.validateSamlAssertion = function(token, done) { - expect(token).to.equal('<trust:RequestedSecurityToken>...</trust:RequestedSecurityToken>'); + strategy._saml.validateSamlAssertion = function(token, _options, done) { + expect(token).to.equal('<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'); done(null, { id: '1234' }); }; - + describe('that was approved', function() { var request, user, info; - + before(function (done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.method = 'POST'; - req.session = {}; - req.session['wsfed:www.example.com'] = {}; - req.session['wsfed:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); - }); - + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['wsfed:www.example.com'] = {}; + req.session['wsfed:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + it('should supply user', function() { expect(user).to.be.an('object'); expect(user.id).to.equal('1234'); }); - + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Hello'); }); - + it('should remove state from session', function() { expect(request.session['wsfed:www.example.com']).to.be.undefined; }); }); - + describe('that was approved with other data in the session', function() { var request, user, info; - + before(function(done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.method = 'POST'; - req.session = {}; - req.session['wsfed:www.example.com'] = {}; - req.session['wsfed:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.session['wsfed:www.example.com'].foo = 'bar'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); - }); - + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['wsfed:www.example.com'] = {}; + req.session['wsfed:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.session['wsfed:www.example.com'].foo = 'bar'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + it('should supply user', function() { expect(user).to.be.an('object'); expect(user.id).to.equal('1234'); }); - + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Hello'); }); - + it('should preserve other data from session', function() { expect(request.session['wsfed:www.example.com'].state).to.be.undefined; expect(request.session['wsfed:www.example.com'].foo).to.equal('bar'); }); }); - + describe('that fails due to state being invalid', function() { var request, info, status; - + before(function (done) { chai.passport.use(strategy) - .fail(function(i, s) { - info = i; - status = s; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; - req.method = 'POST'; - req.session = {}; - req.session['wsfed:www.example.com'] = {}; - req.session['wsfed:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; - }) - .authenticate({}); - }); - + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; + req.method = 'POST'; + req.session = {}; + req.session['wsfed:www.example.com'] = {}; + req.session['wsfed:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + }) + .authenticate({}); + }); + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Invalid authorization request state.'); }); - + it('should supply status', function() { expect(status).to.equal(403); }); - + it('should remove state from session', function() { expect(request.session['wsfed:www.example.com']).to.be.undefined; }); }); - + describe('that fails due to provider-specific state not found in session', function() { var request, info, status; - + before(function(done) { chai.passport.use(strategy) - .fail(function(i, s) { - info = i; - status = s; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; - req.method = 'POST'; - req.session = {}; - }) - .authenticate({}); - }); - + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; + req.method = 'POST'; + req.session = {}; + }) + .authenticate({}); + }); + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Unable to verify authorization request state.'); }); - + it('should supply status', function() { expect(status).to.equal(403); }); }); - + describe('that fails due to provider-specific state lacking state value', function() { var request, info, status; - + before(function(done) { chai.passport.use(strategy) - .fail(function(i, s) { - info = i; - status = s; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; - req.method = 'POST'; - req.session = {}; - req.session['wsfed:www.example.com'] = {}; - }) - .authenticate({}); - }); - + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; + req.method = 'POST'; + req.session = {}; + req.session['wsfed:www.example.com'] = {}; + }) + .authenticate({}); + }); + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Unable to verify authorization request state.'); }); - + it('should supply status', function() { expect(status).to.equal(403); }); }); - + describe('that errors due to lack of session support in app', function() { var request, err; - + before(function (done) { chai.passport.use(strategy) - .error(function(e) { - err = e; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; - req.method = 'POST'; - }) - .authenticate({}); - }); - + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; + req.method = 'POST'; + }) + .authenticate({}); + }); + it('should error', function() { expect(err).to.be.an.instanceof(Error); expect(err.message).to.equal('Authentication requires session support when using state. Did you forget to use express-session middleware?'); }); }); }); }); - + describe('with session key option', function() { var strategy = new Strategy({ - path: '/callback', - realm: 'urn:fixture-test', - identityProviderUrl: 'http://www.example.com/login', - thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], - state: true, - sessionKey: 'wsfed:example' - }, - function (profile, done) { - return done(null, profile, { message: 'Hello' }); - }); + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + state: true, + sessionKey: 'wsfed:example' + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); strategy._wsfed.extractToken = function(req) { expect(req).to.be.an('object'); - return '<trust:RequestedSecurityToken>...</trust:RequestedSecurityToken>'; + return domParser.parseFromString('<trust:RequestedSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestedSecurityToken>', 'text/xml'); }; - strategy._saml.validateSamlAssertion = function(token, done) { - expect(token).to.equal('<trust:RequestedSecurityToken>...</trust:RequestedSecurityToken>'); + strategy._saml.validateSamlAssertion = function(token, _options, done) { + expect(token).to.equal('<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'); done(null, { id: '1234' }); }; - + describe('issuing authorization request', function() { - + describe('that redirects to service provider', function() { var request, url; - + before(function (done) { chai.passport.use(strategy) - .redirect(function(u) { - url = u; - done(); - }) - .req(function(req) { - request = req; - req.session = {}; - }) - .authenticate({}); - }); - + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + }) + .authenticate({}); + }); + it('should be redirected', function() { var u = uri.parse(url, true); expect(u.query.wctx).to.have.length(24); }); - + it('should save state in session', function() { var u = uri.parse(url, true); - + expect(request.session['wsfed:example'].state).to.have.length(24); expect(request.session['wsfed:example'].state).to.equal(u.query.wctx); }); }); }); - + describe('processing response to authorization request', function() { - + describe('that was approved', function() { var request, user, info; - + before(function (done) { chai.passport.use(strategy) - .success(function(u, i) { - user = u; - info = i; - done(); - }) - .req(function(req) { - request = req; - - req.body = {}; - req.body.wresult = '<trust:RequestSecurityTokenResponseCollection>...</trust:RequestSecurityTokenResponseCollection>'; - req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.method = 'POST'; - req.session = {}; - req.session['wsfed:example'] = {}; - req.session['wsfed:example']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; - req.get = function(){ - return ''; - }; - }) - .authenticate({}); - }); - + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">...</trust:RequestSecurityTokenResponseCollection>'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['wsfed:example'] = {}; + req.session['wsfed:example']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + it('should supply user', function() { expect(user).to.be.an('object'); expect(user.id).to.equal('1234'); }); - + it('should supply info', function() { expect(info).to.be.an('object'); expect(info.message).to.equal('Hello'); }); - + it('should remove state from session', function() { expect(request.session['wsfed:example']).to.be.undefined; }); }); }); }); -}); +}); \ No newline at end of file
test/utils.js+172 −14 modified@@ -1,14 +1,13 @@ var expect = require('chai').expect; -var lib = require('../lib/passport-wsfed-saml2'); var utils = require('../lib/passport-wsfed-saml2/utils'); describe('utils', function () { describe('parseSamlAssertion', function () { it('should work', function (done) { var assertion = utils.parseSamlAssertion('<t:RequestSecurityTokenResponse xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"></t:RequestSecurityTokenResponse>'); expect(assertion.childNodes.length) - .to.equal(1); + .to.equal(1); done(); }); @@ -23,9 +22,14 @@ describe('utils', function () { var err = parse(); expect(err.name) - .to.equal('SamlAssertionParserError'); - expect(err.detail) - .to.equal('end tag name: div is not match the current start tagName:t:RequestSecurityTokenResponse'); + .to.equal('SamlAssertionParserError'); + expect(err.detail.message) + .to.equal('Opening and ending tag mismatch: "AssertionAssertion" != "div"'); + done(); + }); + + it('should throw an error with invalid xml = ""<doc><![CDATA[</doc>""', function (done) { + expect(() => { utils.parseSamlAssertion('<doc><![CDATA[</doc>'); }).to.throw('SAML Assertion should be a valid xml'); done(); }); }); @@ -34,7 +38,7 @@ describe('utils', function () { it('should work', function (done) { var response = utils.parseSamlResponse('<t:RequestSecurityTokenResponse xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"></t:RequestSecurityTokenResponse>'); expect(response.childNodes.length) - .to.equal(1); + .to.equal(1); done(); }); @@ -49,9 +53,14 @@ describe('utils', function () { var err = parse(); expect(err.name) - .to.equal('SamlResponseParserError'); - expect(err.detail) - .to.equal('end tag name: div is not match the current start tagName:t:RequestSecurityTokenResponse'); + .to.equal('SamlResponseParserError'); + expect(err.detail.message) + .to.equal('Opening and ending tag mismatch: "AssertionAssertion" != "div"'); + done(); + }); + + it('should throw an error with invalid xml = ""<doc><![CDATA[</doc>""', function (done) { + expect(() => { utils.parseSamlResponse('<doc><![CDATA[</doc>'); }).to.throw('SAMLResponse should be a valid xml'); done(); }); }); @@ -60,7 +69,7 @@ describe('utils', function () { it('should work', function (done) { var response = utils.parseWsFedResponse('<t:RequestSecurityTokenResponse xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"></t:RequestSecurityTokenResponse>'); expect(response.childNodes.length) - .to.equal(1); + .to.equal(1); done(); }); @@ -75,10 +84,159 @@ describe('utils', function () { var err = parse(); expect(err.name) - .to.equal('WSFederationResultParseError'); - expect(err.detail) - .to.equal('end tag name: div is not match the current start tagName:t:RequestSecurityTokenResponse'); + .to.equal('WSFederationResultParseError'); + expect(err.detail.message) + .to.equal('Opening and ending tag mismatch: "AssertionAssertion" != "div"'); + done(); + }); + + it('should throw an error with invalid xml = ""<doc><![CDATA[</doc>""', function (done) { + expect(() => { utils.parseWsFedResponse('<doc><![CDATA[</doc>'); }).to.throw('wresult should be a valid xml'); done(); }); }); -}); + + describe('parseXmlString', () => { + it("should parse XML string", () => { + const xml = "<foo><bar>baz</bar></foo>"; + const result = utils.parseXmlString(xml); + expect(result.documentElement.nodeName).to.equal("foo"); + expect(result.documentElement.firstChild.nodeName).to.equal("bar"); + expect(result.documentElement.firstChild.firstChild.nodeValue).to.equal( + "baz" + ); + }); + + it("should throw on = that is not attached to an attribute", () => { + const xml = "<foo><bar =>baz</bar></foo>"; + expect(() => utils.parseXmlString(xml)).to.throw('Opening and ending tag mismatch: "foo" != "bar"'); + }); + + it("should throw on closing elements without opening elements", () => { + const xml = "<foo></bar></foo>"; + expect(() => utils.parseXmlString(xml)).to.throw('Opening and ending tag mismatch: "foo" != "bar"'); + }); + + describe("CDATA sections", () => { + it("should handle CDATA sections", () => { + const xml = "<foo><![CDATA[<bar>baz</bar>]]></foo>"; + const result = utils.parseXmlString(xml); + expect(result.documentElement.nodeName).to.equal("foo"); + expect(result.documentElement.firstChild.nodeValue).to.equal( + "<bar>baz</bar>" + ); + }); + + it("should not throw CDATA sections on second line", () => { + const xml = `<foo> + <![CDATA[<bar>baz</bar>]]> + </foo>`; + const result = utils.parseXmlString(xml); + expect(result.documentElement.nodeName).to.equal("foo"); + }); + + it("should not throw multi line CDATA section starting on first line", () => { + const xml = `<foo><![CDATA[ + + + This should all be ignored + + ]]> + </foo> + `; + const result = utils.parseXmlString(xml); + expect(result.documentElement.nodeName).to.equal("foo"); + }); + + it("should not throw multi line CDATA section starting on second line", () => { + const xml = `<foo> + <![CDATA[ + + + This should all be ignored + + ]]> + </foo> + `; + const result = utils.parseXmlString(xml); + expect(result.documentElement.nodeName).to.equal("foo"); + }); + + it("should throw if document is single line unclosed CDATA section", () => { + const xml = `<![CDATA[ this is an unclosed CDATA section`; + expect(() => utils.parseXmlString(xml)).to.throw('Invalid CDATA starting at position 0'); + }); + + it("should throw if document is single line closed CDATA section", () => { + const xml = `<![CDATA[ this is a closed CDATA section ]]>`; + expect(() => utils.parseXmlString(xml)).to.throw('CDATA outside of element'); + }); + + it("should throw if document is multi line unclosed CDATA section", () => { + const xml = `<![CDATA[ + this is an unclosed CDATA section + `; + expect(() => utils.parseXmlString(xml)).to.throw('Invalid CDATA starting at position 0'); + }); + + it("should throw with nested CDATA if no start tag", () => { + const xml = `<![CDATA[ + <![CDATA should this be ignored since the other cdata is still open? + this is a closed CDATA section + ]]>`; + expect(() => utils.parseXmlString(xml)).to.throw('CDATA outside of element'); + }); + + it('should not throw if the second CDATA element on a single line is unclosed', () => { + const xml = `<foo> + <![CDATA[ignored]]><![CDATA[ + + just some data + + ]]> + </foo>`; + expect(() => utils.parseXmlString(xml)).to.not.throw(); + }); + + it("should not throw with nested CDATA if with start tag", () => { + const xml = `<foo><![CDATA[ + <![CDATA this CDATA open should be ignored since the other CDATA is still open + this is a closed CDATA section + ]]></foo>`; + expect(() => utils.parseXmlString(xml)).to.not.throw(); + }); + + it("should throw on unclosed CDATA sections on first line", () => { + const xml = "<foo><![CDATA[<bar>baz</bar>></foo>"; + expect(() => utils.parseXmlString(xml)).to.throw("Invalid CDATA starting at position 5"); + }); + + it('should throw on unclosed CDATA sections with closing brackets to finish', () => { + const xml = '<doc><![CDATA[</doc>]]' + expect(() => utils.parseXmlString(xml)).to.throw("Invalid CDATA starting at position 5"); + }); + + it("should throw on lowercase cdata sections", () => { + const xml = "<foo><![cdata[flism</foo>"; + expect(() => utils.parseXmlString(xml)).to.throw(); + }); + + it("should throw on URL encoded XML", () => { + const xml = encodeURIComponent("<foo></foo>"); + expect(() => utils.parseXmlString(xml)).to.throw(); + }); + + it("should throw on base64 encoded XML", () => { + const xml = Buffer.from("<foo><![CDATA[flism></foo>", "utf-8").toString( + "base64" + ); + expect(() => utils.parseXmlString(xml)).to.throw(); + }); + + it("should throw on unclosed CDATA element", () => { + const xml = "<foo><![CDATA[flism></foo>"; + expect(() => utils.parseXmlString(xml)).to.throw(); + }); + }); + }); +}); \ No newline at end of file
test/wsfed.tests.js+54 −29 modified@@ -3,7 +3,7 @@ var server = require('./fixture/wsfed-server'); var request = require('request'); var cheerio = require('cheerio'); const xpath = require('xpath'); -const DOMParser = require('xmldom').DOMParser; +const DOMParser = require('@xmldom/xmldom').DOMParser; describe('wsfed', function () { before(function (done) { @@ -18,18 +18,18 @@ describe('wsfed', function () { it('returns 400 if we have more than one Assertion element', (done) => { request.get({ jar: request.jar(), - uri: 'http://localhost:5050/login?wa=wsignin1.0&wtrealm=urn:fixture-test' + uri: `${server.BASE_URL}/login?wa=wsignin1.0&wtrealm=urn:fixture-test` }, function (err, response, b){ if(err) return done(err); expect(response.statusCode) - .to.equal(200); + .to.equal(200); const $ = cheerio.load(b); const wresult = $('input[name="wresult"]').attr('value'); const wa = $('input[name="wa"]').attr('value'); - const root = new DOMParser().parseFromString(wresult); + const root = new DOMParser().parseFromString(wresult, 'text/xml'); const assertion = xpath.select("//*[local-name(.)='Assertion']", root)[0]; const copiedAssertion = assertion.cloneNode(true); @@ -39,16 +39,41 @@ describe('wsfed', function () { request.post({ jar: request.jar(), - uri: 'http://localhost:5050/callback', + uri: `${server.BASE_URL}/callback`, form: { wresult: modifiedResult, wa: wa } }, function (err, response, _) { if (err) return done(err); expect(response.statusCode).to.equal(400); done(); }); }); - }) + }); + + it(`returns 400 if invalid XML - "<doc><![CDATA[</doc>"`, (done) => { + request.get({ + jar: request.jar(), + uri: `${server.BASE_URL}/login?wa=wsignin1.0&wtrealm=urn:fixture-test` + }, function (err, response, b){ + if(err) return done(err); + expect(response.statusCode) + .to.equal(200); + + + const $ = cheerio.load(b); + const wa = $('input[name="wa"]').attr('value'); + request.post({ + jar: request.jar(), + uri: `${server.BASE_URL}/callback`, + form: { wresult: "<doc><![CDATA[</doc>", wa: wa } + }, function (err, response, _) { + if (err) return done(err); + expect(response.statusCode).to.equal(401); + expect(response.body).to.contain('Unauthorized'); + done(); + }); + }); + }); }) describe('normal flow', function () { @@ -57,11 +82,11 @@ describe('wsfed', function () { before(function (done) { request.get({ jar: request.jar(), - uri: 'http://localhost:5050/login?wa=wsignin1.0&wtrealm=urn:fixture-test' + uri: `${server.BASE_URL}/login?wa=wsignin1.0&wtrealm=urn:fixture-test` }, function (err, response, b){ if(err) return done(err); expect(response.statusCode) - .to.equal(200); + .to.equal(200); $ = cheerio.load(b); @@ -70,7 +95,7 @@ describe('wsfed', function () { request.post({ jar: request.jar(), - uri: 'http://localhost:5050/callback', + uri: `${server.BASE_URL}/callback`, form: { wresult: wresult, wa: wa } }, function(err, response, body) { if(err) return done(err); @@ -84,7 +109,7 @@ describe('wsfed', function () { it('should be valid signature', function(){ expect(r.statusCode) - .to.equal(200); + .to.equal(200); }); it('should return a valid user', function(){ @@ -103,7 +128,7 @@ describe('wsfed', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5050/callback', + uri: `${server.BASE_URL}/callback`, form: { wresult: '<t:RequestSecurityTokenResponse xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"></t:RequestSecurityTokenResponse>' } }, function(err, response, body) { if(err) return done(err); @@ -115,7 +140,7 @@ describe('wsfed', function () { it('should return a 400', function(){ expect(r.statusCode) - .to.equal(400); + .to.equal(400); }); }); @@ -125,7 +150,7 @@ describe('wsfed', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5050/callback' + uri: `${server.BASE_URL}/callback` }, function(err, response, body) { if(err) return done(err); r = response; @@ -136,7 +161,7 @@ describe('wsfed', function () { it('should redirect to idp', function(){ expect(r.statusCode) - .to.equal(302); + .to.equal(302); }); }); @@ -146,7 +171,7 @@ describe('wsfed', function () { before(function (done) { request.post({ jar: request.jar(), - uri: 'http://localhost:5050/callback', + uri: `${server.BASE_URL}/callback`, form: { wresult: 'foo' } }, function(err, response, body) { if(err) return done(err); @@ -158,7 +183,7 @@ describe('wsfed', function () { it('should return a 400', function(){ expect(r.statusCode) - .to.equal(400); + .to.equal(400); }); }); @@ -167,27 +192,27 @@ describe('wsfed', function () { before(function (done) { request.post({ - jar: request.jar(), - uri: 'http://localhost:5050/callback/wresult-with-invalid-xml', - form: { wresult: '<t:RequestSecurityTokenResponse Context="undefined" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"><t:RequestedSecurityToken></saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" MajorVersion="1" MinorVersion="1" AssertionID="_dpEiwydz8xWGlw4HbWMg1XfOUdEpdaMC" IssueInstant="2017-05-24T17:52:42.498Z" Issuer="urn:fixture-test"><saml:Conditions NotBefore="2017-05-24T17:52:42.498Z" NotOnOrAfter="2017-05-25T01:52:42.498Z"><saml:AudienceRestrictionCondition><saml:Audience>urn:fixture-test</saml:Audience></saml:AudienceRestrictionCondition></saml:Conditions><saml:AttributeStatement><saml:Subject><saml:NameIdentifier Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">12345678</saml:NameIdentifier><saml:SubjectConfirmation><saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod></saml:SubjectConfirmation></saml:Subject><saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims" AttributeName="nameidentifier"><saml:AttributeValue>12345678</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims" AttributeName="emailaddress"><saml:AttributeValue>jfoo@gmail.com</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims" AttributeName="name"><saml:AttributeValue>John Foo</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims" AttributeName="givenname"><saml:AttributeValue>John</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims" AttributeName="surname"><saml:AttributeValue>Foo</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthenticationStatement AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password" AuthenticationInstant="2017-05-25T01:52:42.498Z"><saml:Subject><saml:NameIdentifier Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">12345678</saml:NameIdentifier><saml:SubjectConfirmation><saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod></saml:SubjectConfirmation></saml:Subject></saml:AuthenticationStatement><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_dpEiwydz8xWGlw4HbWMg1XfOUdEpdaMC"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>fxxeOWyp3M3cVfglUEg0Fc0mVAm7QVCyrSX2Kflq8VE=</DigestValue></Reference></SignedInfo><SignatureValue>ETv7SqrEoHYP1FTLcdylyDZotyJ1uuNNCLo6sw4cm4YAnGz/OYUIssUb0s82C3NCfV5ifvryr5khnZCNfRvEWJPsIZAtaSPHeeO+x3ajIDd/qfklNBHpdEYMP2WbcqPA6pYeh+OHgAlG6srsLDO8fMymUa/T8yACIU7cwnouEaYESWRU2fqKOXpeUxB/pENiY+qxPTvxzRYld5OlR+sNAJFPIvl3V5G+vw0mx+7tZteKq7yX0djpwEoFfXAcMzvLoqLqENjxPanmVPv7qvv7dIdI0kPE6jret50sHkHpQ7XZJmGi6cNc+/kvhSHXhD3vJ0u3BP/qCCPYPHz42z+KIw==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=</X509Certificate></X509Data></KeyInfo></Signature></saml:Assertion></t:RequestedSecurityToken></t:RequestSecurityTokenResponse>' } - }, - function(err, response, body) { - if(err) return done(err); - r = response; - bod = body; - done(); - }); + jar: request.jar(), + uri: `${server.BASE_URL}/callback/wresult-with-invalid-xml`, + form: { wresult: '<t:RequestSecurityTokenResponse Context="undefined" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"><t:RequestedSecurityToken></saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" MajorVersion="1" MinorVersion="1" AssertionID="_dpEiwydz8xWGlw4HbWMg1XfOUdEpdaMC" IssueInstant="2017-05-24T17:52:42.498Z" Issuer="urn:fixture-test"><saml:Conditions NotBefore="2017-05-24T17:52:42.498Z" NotOnOrAfter="2017-05-25T01:52:42.498Z"><saml:AudienceRestrictionCondition><saml:Audience>urn:fixture-test</saml:Audience></saml:AudienceRestrictionCondition></saml:Conditions><saml:AttributeStatement><saml:Subject><saml:NameIdentifier Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">12345678</saml:NameIdentifier><saml:SubjectConfirmation><saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod></saml:SubjectConfirmation></saml:Subject><saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims" AttributeName="nameidentifier"><saml:AttributeValue>12345678</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims" AttributeName="emailaddress"><saml:AttributeValue>jfoo@gmail.com</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims" AttributeName="name"><saml:AttributeValue>John Foo</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims" AttributeName="givenname"><saml:AttributeValue>John</saml:AttributeValue></saml:Attribute><saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims" AttributeName="surname"><saml:AttributeValue>Foo</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthenticationStatement AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password" AuthenticationInstant="2017-05-25T01:52:42.498Z"><saml:Subject><saml:NameIdentifier Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">12345678</saml:NameIdentifier><saml:SubjectConfirmation><saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod></saml:SubjectConfirmation></saml:Subject></saml:AuthenticationStatement><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_dpEiwydz8xWGlw4HbWMg1XfOUdEpdaMC"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>fxxeOWyp3M3cVfglUEg0Fc0mVAm7QVCyrSX2Kflq8VE=</DigestValue></Reference></SignedInfo><SignatureValue>ETv7SqrEoHYP1FTLcdylyDZotyJ1uuNNCLo6sw4cm4YAnGz/OYUIssUb0s82C3NCfV5ifvryr5khnZCNfRvEWJPsIZAtaSPHeeO+x3ajIDd/qfklNBHpdEYMP2WbcqPA6pYeh+OHgAlG6srsLDO8fMymUa/T8yACIU7cwnouEaYESWRU2fqKOXpeUxB/pENiY+qxPTvxzRYld5OlR+sNAJFPIvl3V5G+vw0mx+7tZteKq7yX0djpwEoFfXAcMzvLoqLqENjxPanmVPv7qvv7dIdI0kPE6jret50sHkHpQ7XZJmGi6cNc+/kvhSHXhD3vJ0u3BP/qCCPYPHz42z+KIw==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=</X509Certificate></X509Data></KeyInfo></Signature></saml:Assertion></t:RequestedSecurityToken></t:RequestSecurityTokenResponse>' } + }, + function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); }); it('should return a 400', function(){ expect(r.statusCode) - .to.equal(400); + .to.equal(400); }); it('should be recognized as an invalid xml', function(){ var err = JSON.parse(bod); expect(err.message) - .to.equal('wresult should be a valid xml');; + .to.match(/^end tag name contains invalid characters/); }); }); -}); +}); \ No newline at end of file
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.