VYPR
Critical severityNVD Advisory· Published May 6, 2025· Updated Apr 15, 2026

CVE-2025-46572

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.

PackageAffected versionsPatched versions
passport-wsfed-saml2npm
>= 3.0.5, < 4.6.44.6.4

Patches

1
e5cf3cc2a537

Upgrades (#190)

https://github.com/auth0/passport-wsfed-saml2Yamil AsustaMay 6, 2025via ghsa
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&#x00FC;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 modified
  • test/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

4

News mentions

0

No linked articles in our index yet.