From 88ec27c285a1e5f6c66c2abbd51ec2acea1f9946 Mon Sep 17 00:00:00 2001 From: mt Date: Sat, 17 Aug 2013 14:35:01 -0400 Subject: [PATCH 01/43] fix xpath lookup --- lib/passport-saml/saml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 0b4ab77a..ca986cfe 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -155,7 +155,7 @@ SAML.prototype.certToPEM = function (cert) { SAML.prototype.validateSignature = function (xml, cert) { var self = this; var doc = new xmldom.DOMParser().parseFromString(xml); - var signature = xmlCrypto.xpath.SelectNodes(doc, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0]; + var signature = xmlCrypto.xpath(doc, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0]; var sig = new xmlCrypto.SignedXml(); sig.keyInfoProvider = { getKeyInfo: function (key) { From 74f964787932b783497410c835704b24866438d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Mon, 16 Sep 2013 14:35:31 +0200 Subject: [PATCH 02/43] fixes redirect initiation for targets that already have ... ... some query string predefined Allow for passive IdP, for example, by adding ?IsPassive=true. --- lib/passport-saml/saml.js | 8 ++++++-- lib/passport-saml/strategy.js | 16 ++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index ca986cfe..f28c3506 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -178,9 +178,13 @@ SAML.prototype.getElement = function (parentElement, elementName) { return parentElement[elementName]; }; -SAML.prototype.validateResponse = function (samlResponse, callback) { +SAML.prototype.validateResponse = function (req, callback) { + var xml = new Buffer(req.body.SAMLResponse, 'base64').toString('ascii'); + return this.validateXML(xml, callback); +}; + +SAML.prototype.validateXML = function (xml, callback) { var self = this; - var xml = new Buffer(samlResponse, 'base64').toString('ascii'); var parser = new xml2js.Parser({explicitRoot:true}); parser.parseString(xml, function (err, doc) { // Verify signature diff --git a/lib/passport-saml/strategy.js b/lib/passport-saml/strategy.js index 95b1a6e8..47e02a33 100644 --- a/lib/passport-saml/strategy.js +++ b/lib/passport-saml/strategy.js @@ -24,11 +24,8 @@ util.inherits(Strategy, passport.Strategy); Strategy.prototype.authenticate = function (req, options) { var self = this; - if (req.body && req.body.SAMLResponse) { - // We have a response, get the user identity out of it - var response = req.body.SAMLResponse; - this._saml.validateResponse(response, function (err, profile, loggedOut) { + function validateCallback(err, profile, loggedOut) { if (err) { return self.error(err); } @@ -36,11 +33,11 @@ Strategy.prototype.authenticate = function (req, options) { if (loggedOut) { if (self._saml.options.logoutRedirect) { self.redirect(self._saml.options.logoutRedirect); - return; + return; } else { - self.redirect("/"); + self.redirect("/"); } - + } var verified = function (err, user, info) { @@ -56,7 +53,10 @@ Strategy.prototype.authenticate = function (req, options) { }; self._verify(profile, verified); - }); + } + + if (req.body && req.body.SAMLResponse) { + this._saml.validateResponse(req, validateCallback); } else { // Initiate new SAML authentication request From 4cee70bee85a3e92ff9bf3f27c1f0ba7e0b00418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Mon, 16 Sep 2013 14:45:53 +0200 Subject: [PATCH 03/43] XML parsing error is not ignored --- lib/passport-saml/saml.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index f28c3506..428473bb 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -187,6 +187,10 @@ SAML.prototype.validateXML = function (xml, callback) { var self = this; var parser = new xml2js.Parser({explicitRoot:true}); parser.parseString(xml, function (err, doc) { + if (err) { + return callback(err, null, false); + } + // Verify signature if (self.options.cert && !self.validateSignature(xml, self.options.cert)) { return callback(new Error('Invalid signature'), null, false); From db6a5ad07f92d1f06bf78c175846434289ded84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Mon, 16 Sep 2013 14:52:54 +0200 Subject: [PATCH 04/43] Forking to have validate{Post,Redirect}Response --- lib/passport-saml/saml.js | 7 ++++++- lib/passport-saml/strategy.js | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 428473bb..388c1559 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -178,7 +178,12 @@ SAML.prototype.getElement = function (parentElement, elementName) { return parentElement[elementName]; }; -SAML.prototype.validateResponse = function (req, callback) { +SAML.prototype.validatePostResponse = function (req, callback) { + var xml = new Buffer(req.body.SAMLResponse, 'base64').toString('ascii'); + return this.validateXML(xml, callback); +}; + +SAML.prototype.validateRedirectResponse = function (req, callback) { var xml = new Buffer(req.body.SAMLResponse, 'base64').toString('ascii'); return this.validateXML(xml, callback); }; diff --git a/lib/passport-saml/strategy.js b/lib/passport-saml/strategy.js index 47e02a33..d234ebc4 100644 --- a/lib/passport-saml/strategy.js +++ b/lib/passport-saml/strategy.js @@ -56,7 +56,9 @@ Strategy.prototype.authenticate = function (req, options) { } if (req.body && req.body.SAMLResponse) { - this._saml.validateResponse(req, validateCallback); + this._saml.validatePostResponse(req, validateCallback); + } else if (req.query && req.query.SAMLResponse) { + this._saml.validateRedirectResponse(req, validateCallback); } else { // Initiate new SAML authentication request From fb401bf5d6f256f0a8ce39354d0a8e3a58a0c8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Mon, 16 Sep 2013 15:13:41 +0200 Subject: [PATCH 05/43] use 'container' parameter, redirect uses req.query not .body --- lib/passport-saml/saml.js | 8 ++++---- lib/passport-saml/strategy.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 388c1559..6cc88ea9 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -178,13 +178,13 @@ SAML.prototype.getElement = function (parentElement, elementName) { return parentElement[elementName]; }; -SAML.prototype.validatePostResponse = function (req, callback) { - var xml = new Buffer(req.body.SAMLResponse, 'base64').toString('ascii'); +SAML.prototype.validatePostResponse = function (container, callback) { + var xml = new Buffer(container.SAMLResponse, 'base64').toString('ascii'); return this.validateXML(xml, callback); }; -SAML.prototype.validateRedirectResponse = function (req, callback) { - var xml = new Buffer(req.body.SAMLResponse, 'base64').toString('ascii'); +SAML.prototype.validateRedirectResponse = function (container, callback) { + var xml = new Buffer(container.SAMLResponse, 'base64').toString('ascii'); return this.validateXML(xml, callback); }; diff --git a/lib/passport-saml/strategy.js b/lib/passport-saml/strategy.js index d234ebc4..74b0d850 100644 --- a/lib/passport-saml/strategy.js +++ b/lib/passport-saml/strategy.js @@ -56,9 +56,9 @@ Strategy.prototype.authenticate = function (req, options) { } if (req.body && req.body.SAMLResponse) { - this._saml.validatePostResponse(req, validateCallback); + this._saml.validatePostResponse(req.body, validateCallback); } else if (req.query && req.query.SAMLResponse) { - this._saml.validateRedirectResponse(req, validateCallback); + this._saml.validateRedirectResponse(req.query, validateCallback); } else { // Initiate new SAML authentication request From 060ce03c62486b368064cd489b91703b2b7b6df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Mon, 16 Sep 2013 15:45:21 +0200 Subject: [PATCH 06/43] correct method called; validateRedirectResponse impl'd --- lib/passport-saml/saml.js | 13 +++++++++++-- lib/passport-saml/strategy.js | 6 +++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 6cc88ea9..d8317fc0 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -184,8 +184,17 @@ SAML.prototype.validatePostResponse = function (container, callback) { }; SAML.prototype.validateRedirectResponse = function (container, callback) { - var xml = new Buffer(container.SAMLResponse, 'base64').toString('ascii'); - return this.validateXML(xml, callback); + var self = this; + var data = new Buffer(container.SAMLResponse, "base64"); + + zlib.inflateRaw(data, function(err, inflated) { + if (err) { + return callback(err); + } + + var xml = inflated.toString("utf8"); + self.validateXML(xml, callback); + }); }; SAML.prototype.validateXML = function (xml, callback) { diff --git a/lib/passport-saml/strategy.js b/lib/passport-saml/strategy.js index 74b0d850..2ea0958f 100644 --- a/lib/passport-saml/strategy.js +++ b/lib/passport-saml/strategy.js @@ -55,10 +55,10 @@ Strategy.prototype.authenticate = function (req, options) { self._verify(profile, verified); } - if (req.body && req.body.SAMLResponse) { - this._saml.validatePostResponse(req.body, validateCallback); - } else if (req.query && req.query.SAMLResponse) { + if (req.query && req.query.SAMLResponse) { this._saml.validateRedirectResponse(req.query, validateCallback); + } else if (req.body && req.body.SAMLResponse) { + this._saml.validatePostResponse(req.body, validateCallback); } else { // Initiate new SAML authentication request From 638ce6e4e257d99bf1f5b59c2b76f0890744ded9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Tue, 17 Sep 2013 11:47:32 +0200 Subject: [PATCH 07/43] accepting redirect as well as post; redirect not verified --- lib/passport-saml/saml.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index d8317fc0..21489987 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -152,10 +152,11 @@ SAML.prototype.certToPEM = function (cert) { return cert; }; -SAML.prototype.validateSignature = function (xml, cert) { +SAML.prototype.validateSignature = function (xml, cert, signature) { var self = this; var doc = new xmldom.DOMParser().parseFromString(xml); - var signature = xmlCrypto.xpath(doc, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0]; + if (signature === "") { return true; } + signature = signature || xmlCrypto.xpath(doc, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0].toString(); var sig = new xmlCrypto.SignedXml(); sig.keyInfoProvider = { getKeyInfo: function (key) { @@ -165,7 +166,7 @@ SAML.prototype.validateSignature = function (xml, cert) { return self.certToPEM(cert); } }; - sig.loadSignature(signature.toString()); + sig.loadSignature(signature); return sig.checkSignature(xml); }; @@ -180,12 +181,14 @@ SAML.prototype.getElement = function (parentElement, elementName) { SAML.prototype.validatePostResponse = function (container, callback) { var xml = new Buffer(container.SAMLResponse, 'base64').toString('ascii'); - return this.validateXML(xml, callback); + return this.validateXML(xml, null, callback); }; SAML.prototype.validateRedirectResponse = function (container, callback) { var self = this; var data = new Buffer(container.SAMLResponse, "base64"); +// TODO verify redirect +// var signature = new Buffer(container.Signature, 'base64').toString('ascii'); zlib.inflateRaw(data, function(err, inflated) { if (err) { @@ -193,11 +196,11 @@ SAML.prototype.validateRedirectResponse = function (container, callback) { } var xml = inflated.toString("utf8"); - self.validateXML(xml, callback); + self.validateXML(xml, "", callback); }); }; -SAML.prototype.validateXML = function (xml, callback) { +SAML.prototype.validateXML = function (xml, signature, callback) { var self = this; var parser = new xml2js.Parser({explicitRoot:true}); parser.parseString(xml, function (err, doc) { @@ -206,7 +209,7 @@ SAML.prototype.validateXML = function (xml, callback) { } // Verify signature - if (self.options.cert && !self.validateSignature(xml, self.options.cert)) { + if (self.options.cert && !self.validateSignature(xml, self.options.cert, signature)) { return callback(new Error('Invalid signature'), null, false); } From 543edbe505c284b1b2d279d8de9cadd768b29f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Thu, 19 Sep 2013 17:08:53 +0200 Subject: [PATCH 08/43] attribute with multiple calues are put as array in profile --- lib/passport-saml/saml.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 0b4ab77a..5af34050 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -223,14 +223,18 @@ SAML.prototype.validateResponse = function (samlResponse, callback) { if (attributes) { attributes.forEach(function (attribute) { var value = self.getElement(attribute, 'AttributeValue'); - if (typeof value[0] === 'string') { - profile[attribute.$.Name] = value[0]; + if (value.length === 1) { + profile[attribute.$.Name] = attrValueMapper(value[0]); } else { - profile[attribute.$.Name] = value[0]._; + profile[attribute.$.Name] = value.map(attrValueMapper); } }); } + function attrValueMapper(value) { + return typeof value === 'string' ? value : value._; + } + if (!profile.mail && profile['urn:oid:0.9.2342.19200300.100.1.3']) { // See http://www.incommonfederation.org/attributesummary.html for definition of attribute OIDs From 320b25258460d050b3340837af284d4604382705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Tue, 17 Sep 2013 16:31:53 +0200 Subject: [PATCH 09/43] After successful logout, clear user and continue --- lib/passport-saml/strategy.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/passport-saml/strategy.js b/lib/passport-saml/strategy.js index 95b1a6e8..ddf0cfc8 100644 --- a/lib/passport-saml/strategy.js +++ b/lib/passport-saml/strategy.js @@ -34,13 +34,8 @@ Strategy.prototype.authenticate = function (req, options) { } if (loggedOut) { - if (self._saml.options.logoutRedirect) { - self.redirect(self._saml.options.logoutRedirect); - return; - } else { - self.redirect("/"); - } - + req.logout(); + return self.pass(); } var verified = function (err, user, info) { From 67632610e71719ce022d9adf07860dea38f80ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Fri, 27 Sep 2013 19:59:42 +0200 Subject: [PATCH 10/43] profile was global because var was missing --- lib/passport-saml/saml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 0b4ab77a..154d2d71 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -195,7 +195,7 @@ SAML.prototype.validateResponse = function (samlResponse, callback) { return callback(new Error('Missing SAML assertion'), null, false); } - profile = {}; + var profile = {}; var issuer = self.getElement(assertion[0], 'Issuer'); if (issuer) { profile.issuer = issuer[0]; From 56e72bcd800b0c769efb7d232b10b70b408f798c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Sat, 28 Sep 2013 15:34:59 +0200 Subject: [PATCH 11/43] Option to tell authenticate what to do without SAMLRe... fields. Now it was always login, but it can be logout as well (or nothing). --- lib/passport-saml/strategy.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/passport-saml/strategy.js b/lib/passport-saml/strategy.js index 95b1a6e8..a9fd2419 100644 --- a/lib/passport-saml/strategy.js +++ b/lib/passport-saml/strategy.js @@ -57,16 +57,26 @@ Strategy.prototype.authenticate = function (req, options) { self._verify(profile, verified); }); - } else { - // Initiate new SAML authentication request - - this._saml.getAuthorizeUrl(req, function (err, url) { + } else if (options.samlFallback) { + // Initiate fallback redirection + + var operation = { + 'login-request': 'getAuthorizeUrl', + 'logout-request': 'getLogoutUrl' + }[options.samlFallback]; + if (!operation) { + return self.fail(); + } + + this._saml[operation](req, function (err, url) { if (err) { return self.fail(); } self.redirect(url); }); + } else { + return self.fail(); } }; From c540661f6e895829feb7d84189ea372dd86c2739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Fri, 27 Sep 2013 20:09:07 +0200 Subject: [PATCH 12/43] validation part of xml contents extracted to parameter --- lib/passport-saml/saml.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 4fd4a262..fcd6293e 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -181,7 +181,7 @@ SAML.prototype.getElement = function (parentElement, elementName) { SAML.prototype.validatePostResponse = function (container, callback) { var xml = new Buffer(container.SAMLResponse, 'base64').toString('ascii'); - return this.validateXML(xml, null, callback); + return this.validateXML(xml, null, validateResponse, callback); }; SAML.prototype.validateRedirectResponse = function (container, callback) { @@ -196,11 +196,11 @@ SAML.prototype.validateRedirectResponse = function (container, callback) { } var xml = inflated.toString("utf8"); - self.validateXML(xml, "", callback); + self.validateXML(xml, "", validateResponse, callback); }); }; -SAML.prototype.validateXML = function (xml, signature, callback) { +SAML.prototype.validateXML = function (xml, signature, validate, callback) { var self = this; var parser = new xml2js.Parser({explicitRoot:true}); parser.parseString(xml, function (err, doc) { @@ -213,6 +213,11 @@ SAML.prototype.validateXML = function (xml, signature, callback) { return callback(new Error('Invalid signature'), null, false); } + validate(self, doc, callback); + }); +}; + +function validateResponse(self, doc, callback) { var response = self.getElement(doc, 'Response'); if (response) { var assertion = self.getElement(response, 'Assertion'); @@ -281,9 +286,6 @@ SAML.prototype.validateXML = function (xml, signature, callback) { } } - - - }); -}; +} exports.SAML = SAML; From bc619a3f517c8bd53b70d132f7953ff911f79b91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Sat, 28 Sep 2013 15:45:34 +0200 Subject: [PATCH 13/43] SAML accepting LogoutRequest, not responding correctly yet --- lib/passport-saml/saml.js | 56 +++++++++++++++++++++++++++++++---- lib/passport-saml/strategy.js | 5 ++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index fcd6293e..eccc9c3c 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -184,19 +184,33 @@ SAML.prototype.validatePostResponse = function (container, callback) { return this.validateXML(xml, null, validateResponse, callback); }; +SAML.prototype.validatePostRequest = function (container, callback) { + var xml = new Buffer(container.SAMLRequest, 'base64').toString('ascii'); + return this.validateXML(xml, null, validateRequest, callback); +}; + SAML.prototype.validateRedirectResponse = function (container, callback) { - var self = this; var data = new Buffer(container.SAMLResponse, "base64"); -// TODO verify redirect -// var signature = new Buffer(container.Signature, 'base64').toString('ascii'); + var signature = null; //new Buffer(container.Signature, 'base64').toString('ascii'); + this.validateRedirect(data, signature, validateResponse, callback); +}; + +SAML.prototype.validateRedirectRequest = function (container, callback) { + var data = new Buffer(container.SAMLRequest, "base64"); + var signature = null; //new Buffer(container.Signature, 'base64').toString('ascii'); + this.validateRedirect(data, signature, validateRequest, callback); +}; + +SAML.prototype.validateRedirect = function(data, signature, validate, callback) { + var self = this; + // TODO verify redirect zlib.inflateRaw(data, function(err, inflated) { if (err) { return callback(err); } - var xml = inflated.toString("utf8"); - self.validateXML(xml, "", validateResponse, callback); + self.validateXML(inflated.toString("utf8"), "", validate, callback); }); }; @@ -288,4 +302,36 @@ function validateResponse(self, doc, callback) { } } +function validateRequest(self, doc, callback) { + var request = self.getElement(doc, 'LogoutRequest'); + if (request) { + var profile = {}; + if (request.$.ID) { + profile.ID = request.$.ID; + } else { + return callback(new Error('Missing SAML LogoutRequest ID'), null, false); + } + var issuer = self.getElement(request, 'Issuer'); + if (issuer) { + profile.issuer = issuer[0]; + } else { + return callback(new Error('Missing SAML issuer'), null, false); + } + + var nameID = self.getElement(request, 'NameID'); + if (nameID) { + profile.nameID = nameID[0]._; + if (nameID[0].$.Format) { + profile.nameIDFormat = nameID[0].$.Format; + } + } else { + return callback(new Error('Missing SAML NameID'), null, false); + } + + callback(null, profile, true); + } else { + return callback(new Error('Unknown SAML request message'), null, false); + } +} + exports.SAML = SAML; diff --git a/lib/passport-saml/strategy.js b/lib/passport-saml/strategy.js index af14b767..df81a7f1 100644 --- a/lib/passport-saml/strategy.js +++ b/lib/passport-saml/strategy.js @@ -31,6 +31,7 @@ Strategy.prototype.authenticate = function (req, options) { } if (loggedOut) { + // TODO logout request case doing logout response generation req.logout(); return self.pass(); } @@ -54,6 +55,10 @@ Strategy.prototype.authenticate = function (req, options) { this._saml.validateRedirectResponse(req.query, validateCallback); } else if (req.body && req.body.SAMLResponse) { this._saml.validatePostResponse(req.body, validateCallback); + } else if (req.query && req.query.SAMLRequest) { + this._saml.validateRedirectRequest(req.query, validateCallback); + } else if (req.body && req.body.SAMLRequest) { + this._saml.validatePostRequest(req.body, validateCallback); } else if (options.samlFallback) { // Initiate fallback redirection From 6cc19a7cd75cd076c2a24fd02ac8897d5beeab6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Sat, 28 Sep 2013 17:29:32 +0200 Subject: [PATCH 14/43] strategy prepared to have logout response functionality --- lib/passport-saml/strategy.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/passport-saml/strategy.js b/lib/passport-saml/strategy.js index df81a7f1..7cbd7dd6 100644 --- a/lib/passport-saml/strategy.js +++ b/lib/passport-saml/strategy.js @@ -31,8 +31,11 @@ Strategy.prototype.authenticate = function (req, options) { } if (loggedOut) { - // TODO logout request case doing logout response generation req.logout(); + if (profile) { + req.samlLogoutRequest = profile; + return self._saml.getLogoutResponseUrl(req, redirectIfSuccess); + } return self.pass(); } @@ -51,6 +54,14 @@ Strategy.prototype.authenticate = function (req, options) { self._verify(profile, verified); } + function redirectIfSuccess(err, url) { + if (err) { + self.fail(); + } else { + self.redirect(url); + } + } + if (req.query && req.query.SAMLResponse) { this._saml.validateRedirectResponse(req.query, validateCallback); } else if (req.body && req.body.SAMLResponse) { @@ -69,14 +80,7 @@ Strategy.prototype.authenticate = function (req, options) { if (!operation) { return self.fail(); } - - this._saml[operation](req, function (err, url) { - if (err) { - return self.fail(); - } - - self.redirect(url); - }); + this._saml[operation](req, redirectIfSuccess); } else { return self.fail(); } From 011622a1dd4c3b01722beff5b93d9840eb100ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Sat, 28 Sep 2013 20:51:15 +0200 Subject: [PATCH 15/43] requestToUrl additionalParameters, req/res --- lib/passport-saml/saml.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 0b4ab77a..bd2dfb31 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -103,9 +103,9 @@ SAML.prototype.generateLogoutRequest = function (req) { return request; }; -SAML.prototype.requestToUrl = function (request, operation, callback) { +SAML.prototype.requestToUrl = function (request, response, operation, additionalParameters, callback) { var self = this; - zlib.deflateRaw(request, function(err, buffer) { + zlib.deflateRaw(request || response, function(err, buffer) { if (err) { return callback(err); } @@ -119,15 +119,20 @@ SAML.prototype.requestToUrl = function (request, operation, callback) { } } - var samlRequest = { + var samlMessage = request ? { SAMLRequest: base64 + } : { + SAMLResponse: base64 }; + Object.keys(additionalParameters).forEach(function(k) { + samlMessage[k] = additionalParameters[k]; + }); if (self.options.privateCert) { - samlRequest.SigAlg = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'; - samlRequest.Signature = self.signRequest(querystring.stringify(samlRequest)); + samlMessage.SigAlg = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'; + samlMessage.Signature = self.signRequest(querystring.stringify(samlMessage)); } - target += querystring.stringify(samlRequest); + target += querystring.stringify(samlMessage); callback(null, target); }); @@ -136,13 +141,13 @@ SAML.prototype.requestToUrl = function (request, operation, callback) { SAML.prototype.getAuthorizeUrl = function (req, callback) { var request = this.generateAuthorizeRequest(req); - this.requestToUrl(request, 'authorize', callback); + this.requestToUrl(request, null, 'authorize', {}, callback); }; SAML.prototype.getLogoutUrl = function(req, callback) { var request = this.generateLogoutRequest(req); - this.requestToUrl(request, 'logout', callback); + this.requestToUrl(request, null, 'logout', {}, callback); }; SAML.prototype.certToPEM = function (cert) { From 941003397ffa90cf868db68a71febe68ca46b6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Sat, 28 Sep 2013 17:51:18 +0200 Subject: [PATCH 16/43] generating LogoutResponse --- lib/passport-saml/saml.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 88cbafc2..f8acc7ef 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -103,6 +103,26 @@ SAML.prototype.generateLogoutRequest = function (req) { return request; }; +SAML.prototype.generateLogoutResponse = function (req, logoutRequest) { + var id = "_" + this.generateUniqueID(); + var instant = this.generateInstant(); + +// +// https://sts.windows.net/82869000-6ad1-48f0-8171-272ed18796e9/ +// +// +// +// + + var request = "" + + "" + this.options.issuer + ""+ + ""+ + ""; + return request; +}; + SAML.prototype.requestToUrl = function (request, response, operation, additionalParameters, callback) { var self = this; zlib.deflateRaw(request || response, function(err, buffer) { @@ -150,6 +170,12 @@ SAML.prototype.getLogoutUrl = function(req, callback) { this.requestToUrl(request, null, 'logout', {}, callback); }; +SAML.prototype.getLogoutResponseUrl = function(req, callback) { + var response = this.generateLogoutResponse(req, req.samlLogoutRequest); + + this.requestToUrl(null, response, 'logout', {}, callback); +}; + SAML.prototype.certToPEM = function (cert) { cert = cert.match(/.{1,64}/g).join('\n'); cert = "-----BEGIN CERTIFICATE-----\n" + cert; From 10bcee137c421132bc354d0461588c5437df6109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Sat, 28 Sep 2013 20:51:15 +0200 Subject: [PATCH 17/43] using RelayState --- lib/passport-saml/saml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index f8acc7ef..96ea2f2f 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -173,7 +173,7 @@ SAML.prototype.getLogoutUrl = function(req, callback) { SAML.prototype.getLogoutResponseUrl = function(req, callback) { var response = this.generateLogoutResponse(req, req.samlLogoutRequest); - this.requestToUrl(null, response, 'logout', {}, callback); + this.requestToUrl(null, response, 'logout', req.query && req.query.RelayState ? { RelayState: req.query.RelayState } : {}, callback); }; SAML.prototype.certToPEM = function (cert) { From 230db7cbb9961ee9d1ca756bdf048dda7efcc5f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Sat, 28 Sep 2013 22:07:42 +0200 Subject: [PATCH 18/43] Generating passive AuthnRequest if options say so --- lib/passport-saml/saml.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 0b4ab77a..16016c02 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -53,7 +53,7 @@ SAML.prototype.signRequest = function (xml) { return signer.sign(this.options.privateCert, 'base64'); }; -SAML.prototype.generateAuthorizeRequest = function (req) { +SAML.prototype.generateAuthorizeRequest = function (req, isPassive) { var id = "_" + this.generateUniqueID(); var instant = this.generateInstant(); var callbackUrl; @@ -68,7 +68,7 @@ SAML.prototype.generateAuthorizeRequest = function (req) { var request = "" + + this.options.entryPoint + (isPassive ? "\" IsPassive=\"true" : "") + "\">" + "" + this.options.issuer + "\n"; if (this.options.identifierFormat) { @@ -134,7 +134,7 @@ SAML.prototype.requestToUrl = function (request, operation, callback) { }; SAML.prototype.getAuthorizeUrl = function (req, callback) { - var request = this.generateAuthorizeRequest(req); + var request = this.generateAuthorizeRequest(req, this.options.passive); this.requestToUrl(request, 'authorize', callback); }; From 958f5d8934acc4e593579454481cfca57c07f622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Sun, 29 Sep 2013 19:29:46 +0200 Subject: [PATCH 19/43] Accepting NoPassive SAML Response --- lib/passport-saml/saml.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 16016c02..e3e7f6ac 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -192,6 +192,16 @@ SAML.prototype.validateResponse = function (samlResponse, callback) { if (response) { var assertion = self.getElement(response, 'Assertion'); if (!assertion) { + var status = self.getElement(response, 'Status'); + if (status) { + status = self.getElement(status[0], 'StatusCode'); + if (status && status[0].$.Value === "urn:oasis:names:tc:SAML:2.0:status:Responder") { + status = self.getElement(status[0], 'StatusCode'); + if (status && status[0].$.Value === "urn:oasis:names:tc:SAML:2.0:status:NoPassive") { + return callback(null, null, false); + } + } + } return callback(new Error('Missing SAML assertion'), null, false); } From 4b810ab1760b773d5a906aabaf0ed6b24f50929f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Sun, 29 Sep 2013 23:07:54 +0200 Subject: [PATCH 20/43] throwing if unknown operation --- lib/passport-saml/saml.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index bd2dfb31..73add7cb 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -117,6 +117,8 @@ SAML.prototype.requestToUrl = function (request, response, operation, additional if (self.options.logoutUrl) { target = self.options.logoutUrl + '?'; } + } else if (operation !== 'authorize') { + return callback(new Error("Unknown operation: "+operation)); } var samlMessage = request ? { From 79f2f98cb91cdf51a32761ff5d399a59ca5783a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Sun, 3 Nov 2013 09:30:41 +0100 Subject: [PATCH 21/43] Forking the name to upload it to npm. --- package.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b966e02b..6e354bf3 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { - "name": "passport-saml", - "version": "0.0.4", + "name": "passport-saml-too", + "version": "0.0.5", "licenses": [{ "type": "MIT", - "url": "https://github.com/bergie/passport-saml/raw/master/LICENSE" + "url": "https://github.com/herby/passport-saml/raw/master/LICENSE" }], "keywords": ["saml", "adfs", "sso", "shibboleth"], "description": "SAML 2.0 authentication strategy for Passport", @@ -13,7 +13,8 @@ "url": "http://bergie.iki.fi" }, "contributors" : [ - "Michael Bosworth" + "Michael Bosworth", + "Herbert Vojčík" ], "main": "./lib/passport-saml", "dependencies": { From 7dbd833b6e6f2acb43941da4b272371341148291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Mon, 30 Dec 2013 20:49:58 +0100 Subject: [PATCH 22/43] Passing RelayState in all SAML operations. --- lib/passport-saml/saml.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 4ec312ad..edc2fd21 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -162,20 +162,20 @@ SAML.prototype.requestToUrl = function (request, response, operation, additional SAML.prototype.getAuthorizeUrl = function (req, callback) { var request = this.generateAuthorizeRequest(req, this.options.passive); - - this.requestToUrl(request, null, 'authorize', {}, callback); + var RelayState = req.query && req.query.RelayState || req.body && req.body.RelayState; + this.requestToUrl(request, null, 'authorize', RelayState ? { RelayState: RelayState } : {}, callback); }; SAML.prototype.getLogoutUrl = function(req, callback) { var request = this.generateLogoutRequest(req); - - this.requestToUrl(request, null, 'logout', {}, callback); + var RelayState = req.query && req.query.RelayState || req.body && req.body.RelayState; + this.requestToUrl(request, null, 'logout', RelayState ? { RelayState: RelayState } : {}, callback); }; SAML.prototype.getLogoutResponseUrl = function(req, callback) { var response = this.generateLogoutResponse(req, req.samlLogoutRequest); - - this.requestToUrl(null, response, 'logout', req.query && req.query.RelayState ? { RelayState: req.query.RelayState } : {}, callback); + var RelayState = req.query && req.query.RelayState || req.body && req.body.RelayState; + this.requestToUrl(null, response, 'logout', RelayState ? { RelayState: RelayState } : {}, callback); }; SAML.prototype.certToPEM = function (cert) { From 38d869244c185eb2fe919681a8f8c7900a81a0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herbert=20Voj=C4=8D=C3=ADk?= Date: Mon, 30 Dec 2013 20:50:28 +0100 Subject: [PATCH 23/43] bump version 0.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6e354bf3..77aae1f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "passport-saml-too", - "version": "0.0.5", + "version": "0.0.6", "licenses": [{ "type": "MIT", "url": "https://github.com/herby/passport-saml/raw/master/LICENSE" From a293dc65d88867887e1bda3e277a005040eab419 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 21 May 2014 10:41:13 -0700 Subject: [PATCH 24/43] Treat a missing 'AttributeStatement' as just a lack of attributes instead of an error. --- lib/passport-saml/saml.js | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index edc2fd21..e4a0f3da 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -301,25 +301,23 @@ function validateResponse(self, doc, callback) { } var attributeStatement = self.getElement(assertion[0], 'AttributeStatement'); - if (!attributeStatement) { - return callback(new Error('Missing AttributeStatement'), null, false); - } - - var attributes = self.getElement(attributeStatement[0], 'Attribute'); - - if (attributes) { - attributes.forEach(function (attribute) { - var value = self.getElement(attribute, 'AttributeValue'); - if (value.length === 1) { - profile[attribute.$.Name] = attrValueMapper(value[0]); - } else { - profile[attribute.$.Name] = value.map(attrValueMapper); - } - }); - } + if (attributeStatement) { + var attributes = self.getElement(attributeStatement[0], 'Attribute'); + + if (attributes) { + attributes.forEach(function (attribute) { + var value = self.getElement(attribute, 'AttributeValue'); + if (value.length === 1) { + profile[attribute.$.Name] = attrValueMapper(value[0]); + } else { + profile[attribute.$.Name] = value.map(attrValueMapper); + } + }); + } - function attrValueMapper(value) { - return typeof value === 'string' ? value : value._; + function attrValueMapper(value) { + return typeof value === 'string' ? value : value._; + } } From 7086e3f0b7c1202a0d2206927b8fef22f026abe3 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Tue, 27 May 2014 14:30:15 -0700 Subject: [PATCH 25/43] Check some extra namespaces needed to handle Okta assertions. (see https://github.com/qingdou/passport-saml/commit/390bbf188d2ff329251ebc13f43b8047e1d5de91) --- lib/passport-saml/saml.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index e4a0f3da..b464494f 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -204,10 +204,12 @@ SAML.prototype.validateSignature = function (xml, cert, signature) { }; SAML.prototype.getElement = function (parentElement, elementName) { - if (parentElement['saml:' + elementName]) { - return parentElement['saml:' + elementName]; - } else if (parentElement['samlp:'+elementName]) { - return parentElement['samlp:'+elementName]; + // once using xml2js 0.4.1+, can just use a tag name processor + var namespacePrefixesToCheck = ['saml', 'samlp', 'saml2', 'saml2p']; + for(var i=0; i < namespacePrefixesToCheck.length; i++ ) { + var potentialElement = parentElement[namespacePrefixesToCheck[i] + ':' + elementName]; + if ( potentialElement ) + return potentialElement; } return parentElement[elementName]; }; From 3a9a8cb57fe88de16c991ec0e8b59617172d1904 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 14:39:41 -0700 Subject: [PATCH 26/43] Adding some basic tests. --- package.json | 9 ++++- test/mocha.opts | 1 + test/tests.js | 101 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 test/mocha.opts create mode 100644 test/tests.js diff --git a/package.json b/package.json index 77aae1f0..f1d6a86b 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,16 @@ "xmldom": "0.1.x" }, "devDependencies": { - "express": "2.5.x", + "express": "4.x", "ejs": "0.7.x", - "jshint": "*" + "jshint": "*", + "request": "*", + "should": "*" }, "engines": { "node": ">= 0.6.0" + }, + "scripts" : { + "test": "mocha" } } diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 00000000..fb5f8242 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1 @@ +--reporter spec \ No newline at end of file diff --git a/test/tests.js b/test/tests.js new file mode 100644 index 00000000..a0ed373c --- /dev/null +++ b/test/tests.js @@ -0,0 +1,101 @@ +'use strict'; +var express = require( 'express' ); +var bodyParser = require( 'body-parser' ); +var passport = require( 'passport' ); +var SamlStrategy = require( '../lib/passport-saml/index.js' ).Strategy; +var request = require( 'request' ); +var should = require( 'should' ); + +describe( 'passport-saml /', function() { + describe( 'captured saml responses /', function() { + var capturedChecks = [ + { name: 'Okta -- valid config should succeed', + samlResponse: { + SAMLResponse: 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cDovL2xvY2FsaG9zdC9icm93c2VyU2FtbExvZ2luIiBJRD0iaWQzMzcxMDA5NzQ3ODg2OTIzMjIwMzA1Njc3ODMiIEluUmVzcG9uc2VUbz0iXzVjMzg1YWJiMTc3MzViN2FhOGQxIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDUtMjdUMjM6Mjc6MzUuNDI2WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSI+aHR0cDovL3d3dy5va3RhLmNvbS9rdmpqNDZsc0RRRVFZVURCWklZVzwvc2FtbDI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjaWQzMzcxMDA5NzQ3ODg2OTIzMjIwMzA1Njc3ODMiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPjRSNUNjYUVEQ3dPSCtudnVHSkY3TWRBelpIdz08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+TzlycW1TUTJQd1NaZDFBeklRUDUySkY5VHBaSkJUNENpdnAxTUZZN3FzODdrU2RFQ3dNbWFWTDE2NFI4dUlCbzdscXUwOFRaTGZYeGF6ak5uRDhnV05BOWRuK2d5cFc1ZWo4S3EzK1J3cUxZUzM1M3pCeVpWcy9MSUxQZVJCK0tUS2RsbTVJSDcxdWhwbUp4a3k0T0gwNWdDSWExYnRFaXpKV1h3d1lleXpBPTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ29UQ0NBZ3FnQXdJQkFnSUdBVVk4elZQWU1BMEdDU3FHU0liM0RRRUJCUVVBTUlHVE1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFRwpBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFQ2d3RVQydDBZVEVVCk1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhGREFTQmdOVkJBTU1DM04xWW5Od1lXTmxjM2N4TVJ3d0dnWUpLb1pJaHZjTkFRa0IKRmcxcGJtWnZRRzlyZEdFdVkyOXRNQjRYRFRFME1EVXlOekE0TWpreU4xb1hEVFEwTURVeU56QTRNekF5TjFvd2daTXhDekFKQmdOVgpCQVlUQWxWVE1STXdFUVlEVlFRSURBcERZV3hwWm05eWJtbGhNUll3RkFZRFZRUUhEQTFUWVc0Z1JuSmhibU5wYzJOdk1RMHdDd1lEClZRUUtEQVJQYTNSaE1SUXdFZ1lEVlFRTERBdFRVMDlRY205MmFXUmxjakVVTUJJR0ExVUVBd3dMYzNWaWMzQmhZMlZ6ZHpFeEhEQWEKQmdrcWhraUc5dzBCQ1FFV0RXbHVabTlBYjJ0MFlTNWpiMjB3Z1o4d0RRWUpLb1pJaHZjTkFRRUJCUUFEZ1kwQU1JR0pBb0dCQUtZWAovVWRWOUhMWGJOS3YweWM2WUpCdHpkV051R1RQSVhXVlBDYjBPc1J2UjU0bHErMFNMUFFHSlMzOWZsV0tpcXRVaUV1c3VpaDZHZExmCk52RkFWTEdQcUJUV1E0TGM3cjUrekQ1cW5INkxpVE0xS0NiYUYzdXUyUCtUK3V0c0dxRlVaYXdBOUZFVk5Ma0lDZTM2WDF5RTQwWCsKL2pNeWd5aWVRUE1lSkFCdkFnTUJBQUV3RFFZSktvWklodmNOQVFFRkJRQURnWUVBSFNXUEc0eTFiTnlKeVNnYkZxRGNTLzZNVU5JcgpTZmtRWUl4eklHUTM3cXk1cUFXMTVZa0JVaVc5VHNHSlJjcmd1c1ZyWklpYTE4ZVl6ODRabjdGeVVaTk1GbzI0TDdsNkJTMFdDaXRJClFTY0JDS0I0UVdBL2Ixc3pxczdFdHBPY1BQMDFUWU5xMnJhcytWcVIzYVdBWExYMm9LZkNsVi9TVXdzelJmNVU4NVk9PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWwycDpTdGF0dXMgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1sMnA6U3RhdHVzPjxzYW1sMjpBc3NlcnRpb24geG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJpZDMzNzEwMDk3NDc4OTQ5MTAwNjY2NTIyNTEyIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDUtMjdUMjM6Mjc6MzUuNDI2WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5IiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cDovL3d3dy5va3RhLmNvbS9rdmpqNDZsc0RRRVFZVURCWklZVzwvc2FtbDI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjaWQzMzcxMDA5NzQ3ODk0OTEwMDY2NjUyMjUxMiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+UFM4dE5taVVZUXpRQVFURjh3RDFRT3dMazA0PTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5XM3JiUkNFUmE1ZEtUcy9MSERwY0pEeUhjSzFESjczd2xhT3J2MXZWNmZxQ3FTTUNpclZLRVlLKzR2OG5WaThZNlZwcVJHd0trWFV2cUY0b0hKWjYzOE5YYVRiRVRjU1VXWjVUNXowbzBNVXhBL3RnL01zVzYvRlVRMXBqUWJhVlhJT3Mvc1EwVzluUXpmZkVZLzgrSkVJMldLY0I2UkZlVzVtTlkvdm9oUWM9PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDb1RDQ0FncWdBd0lCQWdJR0FVWTh6VlBZTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUdUTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHCkExVUVDQXdLUTJGc2FXWnZjbTVwWVRFV01CUUdBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVOTUFzR0ExVUVDZ3dFVDJ0MFlURVUKTUJJR0ExVUVDd3dMVTFOUFVISnZkbWxrWlhJeEZEQVNCZ05WQkFNTUMzTjFZbk53WVdObGMzY3hNUnd3R2dZSktvWklodmNOQVFrQgpGZzFwYm1adlFHOXJkR0V1WTI5dE1CNFhEVEUwTURVeU56QTRNamt5TjFvWERUUTBNRFV5TnpBNE16QXlOMW93Z1pNeEN6QUpCZ05WCkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SWXdGQVlEVlFRSERBMVRZVzRnUm5KaGJtTnBjMk52TVEwd0N3WUQKVlFRS0RBUlBhM1JoTVJRd0VnWURWUVFMREF0VFUwOVFjbTkyYVdSbGNqRVVNQklHQTFVRUF3d0xjM1ZpYzNCaFkyVnpkekV4SERBYQpCZ2txaGtpRzl3MEJDUUVXRFdsdVptOUFiMnQwWVM1amIyMHdnWjh3RFFZSktvWklodmNOQVFFQkJRQURnWTBBTUlHSkFvR0JBS1lYCi9VZFY5SExYYk5LdjB5YzZZSkJ0emRXTnVHVFBJWFdWUENiME9zUnZSNTRscSswU0xQUUdKUzM5ZmxXS2lxdFVpRXVzdWloNkdkTGYKTnZGQVZMR1BxQlRXUTRMYzdyNSt6RDVxbkg2TGlUTTFLQ2JhRjN1dTJQK1QrdXRzR3FGVVphd0E5RkVWTkxrSUNlMzZYMXlFNDBYKwovak15Z3lpZVFQTWVKQUJ2QWdNQkFBRXdEUVlKS29aSWh2Y05BUUVGQlFBRGdZRUFIU1dQRzR5MWJOeUp5U2diRnFEY1MvNk1VTklyClNma1FZSXh6SUdRMzdxeTVxQVcxNVlrQlVpVzlUc0dKUmNyZ3VzVnJaSWlhMThlWXo4NFpuN0Z5VVpOTUZvMjRMN2w2QlMwV0NpdEkKUVNjQkNLQjRRV0EvYjFzenFzN0V0cE9jUFAwMVRZTnEycmFzK1ZxUjNhV0FYTFgyb0tmQ2xWL1NVd3N6UmY1VTg1WT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDI6U3ViamVjdCB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+PHNhbWwyOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+YmVuQHN1YnNwYWNlc3cuY29tPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJfNWMzODVhYmIxNzczNWI3YWE4ZDEiIE5vdE9uT3JBZnRlcj0iMjAxNC0wNS0yN1QyMzozMjozNS40MjZaIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2NhbGhvc3QvYnJvd3NlclNhbWxMb2dpbiIvPjwvc2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWwyOlN1YmplY3Q+PHNhbWwyOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE0LTA1LTI3VDIzOjIyOjM1LjQyNloiIE5vdE9uT3JBZnRlcj0iMjAxNC0wNS0yN1QyMzozMjozNS40MjZaIiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+PHNhbWwyOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWwyOkF1ZGllbmNlPmh0dHBzOi8vYWRtaW4uc3Vic3BhY2Vzdy5jb208L3NhbWwyOkF1ZGllbmNlPjwvc2FtbDI6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWwyOkNvbmRpdGlvbnM+PHNhbWwyOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wNS0yN1QyMzoyNzozNS40MjZaIiBTZXNzaW9uSW5kZXg9Il81YzM4NWFiYjE3NzM1YjdhYThkMSIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdXRobkNvbnRleHQ+PHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlg1MDk8L3NhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDI6QXV0aG5Db250ZXh0Pjwvc2FtbDI6QXV0aG5TdGF0ZW1lbnQ+PC9zYW1sMjpBc3NlcnRpb24+PC9zYW1sMnA6UmVzcG9uc2U+', + RelayState: '', + }, + config: { + entryPoint: 'https://subspacesw1.okta.com/app/subspacesw_subspacetest_1/kvjj46lsDQEQYUDBZIYW/sso/saml', + cert: 'MIICoTCCAgqgAwIBAgIGAUY8zVPYMA0GCSqGSIb3DQEBBQUAMIGTMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFDASBgNVBAMMC3N1YnNwYWNlc3cxMRwwGgYJKoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMB4XDTE0MDUyNzA4MjkyN1oXDTQ0MDUyNzA4MzAyN1owgZMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKDARPa3RhMRQwEgYDVQQLDAtTU09Qcm92aWRlcjEUMBIGA1UEAwwLc3Vic3BhY2VzdzExHDAaBgkqhkiG9w0BCQEWDWluZm9Ab2t0YS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKYX/UdV9HLXbNKv0yc6YJBtzdWNuGTPIXWVPCb0OsRvR54lq+0SLPQGJS39flWKiqtUiEusuih6GdLfNvFAVLGPqBTWQ4Lc7r5+zD5qnH6LiTM1KCbaF3uu2P+T+utsGqFUZawA9FEVNLkICe36X1yE40X+/jMygyieQPMeJABvAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAHSWPG4y1bNyJySgbFqDcS/6MUNIrSfkQYIxzIGQ37qy5qAW15YkBUiW9TsGJRcrgusVrZIia18eYz84Zn7FyUZNMFo24L7l6BS0WCitIQScBCKB4QWA/b1szqs7EtpOcPP01TYNq2ras+VqR3aWAXLX2oKfClV/SUwszRf5U85Y=' + }, + expectedStatusCode: 200, + expectedNameIDStartsWith: 'ben' + }, + { name: 'Onelogin -- invalid cert (from Okta case) should fail', + samlResponse: { + SAMLResponse: 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0\r\nYzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6\r\nbmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJSNjg5YjA3MzNiY2Nj\r\nYTIyYTEzN2UzNjU0ODMwMzEyMzMyOTQwYjFiZSIgVmVyc2lvbj0iMi4wIiBJ\r\nc3N1ZUluc3RhbnQ9IjIwMTQtMDUtMjhUMDA6MTY6MDhaIiBEZXN0aW5hdGlv\r\nbj0ie3JlY2lwaWVudH0iIEluUmVzcG9uc2VUbz0iX2E2ZmM0NmJlODRlMWUz\r\nY2YzYzUwIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29t\r\nL3NhbWwvbWV0YWRhdGEvMzcxNzU1PC9zYW1sOklzc3Vlcj48c2FtbHA6U3Rh\r\ndHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6\r\ndGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48\r\nc2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIw\r\nMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIw\r\nMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBWZXJzaW9uPSIyLjAiIElEPSJwZngz\r\nYjYzYzdiZS1mZTg2LTYyZmQtOGNiNS0xNmFiNjI3M2VmYWEiIElzc3VlSW5z\r\ndGFudD0iMjAxNC0wNS0yOFQwMDoxNjowOFoiPjxzYW1sOklzc3Vlcj5odHRw\r\nczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbC9tZXRhZGF0YS8zNzE3NTU8L3Nh\r\nbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cu\r\ndzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpD\r\nYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53\r\nMy5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1l\r\ndGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1s\r\nZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4M2I2M2M3\r\nYmUtZmU4Ni02MmZkLThjYjUtMTZhYjYyNzNlZmFhIj48ZHM6VHJhbnNmb3Jt\r\ncz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcv\r\nMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJh\r\nbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94\r\nbWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRo\r\nb2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRz\r\naWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5EQ25QVFFZQmIxaEtzcGJlNmZn\r\nMVUzcTh4bjQ9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2Rz\r\nOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPmUwK2FGb21BMCtKQVkw\r\nZjl0S3F6SXVxSVZTU3c3TGlGVXNuZUVES1BCV2RpVHoxc01kZ3IvMnkxZTkr\r\ncmphUzJtUm1DaS92U1FMWTN6VFl6MGhwNm5KTlUxOStUV29YbzlrSFF5V1Q0\r\nS2tlUUw0WHMvZ1ovQW9LQzIwaUhWS3RwUHBzMElRME1sL3FSb291U2l0dDZT\r\nZi9XRHoyTFYvcFdjSDJoeDV0djN4U3czNmhLMk5RYzdxdzdyMW1FWG52Y2pY\r\nUmVZbzhyclZmN1hIR0d4Tm9SSUVJQ1VJaTExMHV2c1dlbVNYZjBaMGR5YjBG\r\nVllPV3VTc1FNRGx6TnBoZUFEQmlmRk80VVRmU0VoRlp2bjhrVkNHWlVJd3Ji\r\nT2haMmQvK1lFdGd5dVRnK3F0c2xnZnk0ZHdkNFR2RWNmdVJ6UVRhemVlZnBy\r\nU0Z5aVFja0FYT2pjdz09PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5m\r\nbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRnpDQ0F2\r\nK2dBd0lCQWdJVUZKc1VqUE03QW1Xdk50RXZVTFNIbFRUTWlMUXdEUVlKS29a\r\nSWh2Y05BUUVGQlFBd1dERUxNQWtHQTFVRUJoTUNWVk14RVRBUEJnTlZCQW9N\r\nQ0ZOMVluTndZV05sTVJVd0V3WURWUVFMREF4UGJtVk1iMmRwYmlCSlpGQXhI\r\nekFkQmdOVkJBTU1Gazl1WlV4dloybHVJRUZqWTI5MWJuUWdOREl6TkRrd0ho\r\nY05NVFF3TlRFek1UZ3dOakV5V2hjTk1Ua3dOVEUwTVRnd05qRXlXakJZTVFz\r\nd0NRWURWUVFHRXdKVlV6RVJNQThHQTFVRUNnd0lVM1ZpYzNCaFkyVXhGVEFU\r\nQmdOVkJBc01ERTl1WlV4dloybHVJRWxrVURFZk1CMEdBMVVFQXd3V1QyNWxU\r\nRzluYVc0Z1FXTmpiM1Z1ZENBME1qTTBPVENDQVNJd0RRWUpLb1pJaHZjTkFR\r\nRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFLckF6SmRZOUZ6Rkx0NWJsQXJKZlB6\r\nZ2k4N0VuRkdsVGZjVjVUMVRVRHdMQmxEa1kvMFpHS25NT3BmM0Q3aWUyQzRw\r\nUEZPSW1Pb2djTTVrcERETDdxeFRYWjFld1hWeWpCZE11MjlORzJDNk56V2VR\r\nVFVNVWppMDFFY0hrQzhvK1B0czhBTmlOT1ljanhFZXloRXl6SktnRWl6YmxZ\r\nek1NS3pkck9FVDZRdXFXbzNDODNLKzUrNWRzakRuMW9vS0dSd2ozSHZnc1lj\r\nRnJRbDlOb2pnUUZqb29id3NpRS83QStPSmhMcEJjeS9uU1Znbm9KYU1mck8r\r\nSnNudWtaUHp0Ym50THZPbDU2K1ZyYTBOOG41TkFZaGFTYXlQaXYvYXloalZn\r\namZYZDF0ak1WVE9pRGtuVU93aXpadUoxWTNRSDk0dlV0QmdwMFdCcEJTcy94\r\nTXlUczhDQXdFQUFhT0IyRENCMVRBTUJnTlZIUk1CQWY4RUFqQUFNQjBHQTFV\r\nZERnUVdCQlJRTzRXcE01Zld3eGliNDlXVHVKa2ZZRGJ4T0RDQmxRWURWUjBq\r\nQklHTk1JR0tnQlJRTzRXcE01Zld3eGliNDlXVHVKa2ZZRGJ4T0tGY3BGb3dX\r\nREVMTUFrR0ExVUVCaE1DVlZNeEVUQVBCZ05WQkFvTUNGTjFZbk53WVdObE1S\r\nVXdFd1lEVlFRTERBeFBibVZNYjJkcGJpQkpaRkF4SHpBZEJnTlZCQU1NRms5\r\ndVpVeHZaMmx1SUVGalkyOTFiblFnTkRJek5EbUNGQlNiRkl6ek93SmxyemJS\r\nTDFDMGg1VTB6SWkwTUE0R0ExVWREd0VCL3dRRUF3SUhnREFOQmdrcWhraUc5\r\ndzBCQVFVRkFBT0NBUUVBQ2REQUFvYVpGQ0VZNXBtZndiS3VLclh0TzVpRThs\r\nV3RpQ1BqQ1pFVXVUNmJYUk5jcXJkbnVWL0VBZlg5V1FvWGphbFBpMGVNNzh6\r\nS21idlJHU1RVSHdXdzQ5UkhqRmZlSlVLdkhOZU5uRmdUWERqRVBOaE12aDY5\r\na0htNDUzbEZSbUIra2s2eWp0WFJaYVFFd1M4VXVvMk90K2tyZ05ibDZvVEJa\r\nSjBBSEgxTXRaRUNEbG9tczFLbTd6c0s4d0FpNWk4VFZJS2tWcjViMlZsaHJM\r\nZ0ZNdnpaNVZpQXhJTUdCNnc0N3lZNFFHUUIvNVE4eWE5aEJzOXZrbit3dWJB\r\nK3lyNGoxNEpYWjdibFZLRFNUWXZhNjVFYStQcUh5cnArV25tbmJ3Mk9iUzdp\r\nV2V4aVR5MWpEM0cwUjJhdkRCRmpNOEZqNURiZnVmc0UxYjBVMTBSVHRnPT08\r\nL2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5m\r\nbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBG\r\nb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9y\r\nbWF0OnRyYW5zaWVudCI+cGxvZXJAc3Vic3BhY2Vzdy5jb208L3NhbWw6TmFt\r\nZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2Fz\r\naXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0\r\nQ29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTQtMDUtMjhUMDA6\r\nMTk6MDhaIiBSZWNpcGllbnQ9IntyZWNpcGllbnR9IiBJblJlc3BvbnNlVG89\r\nIl9hNmZjNDZiZTg0ZTFlM2NmM2M1MCIvPjwvc2FtbDpTdWJqZWN0Q29uZmly\r\nbWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVm\r\nb3JlPSIyMDE0LTA1LTI4VDAwOjEzOjA4WiIgTm90T25PckFmdGVyPSIyMDE0\r\nLTA1LTI4VDAwOjE5OjA4WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48\r\nc2FtbDpBdWRpZW5jZT57YXVkaWVuY2V9PC9zYW1sOkF1ZGllbmNlPjwvc2Ft\r\nbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1s\r\nOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wNS0yOFQwMDox\r\nNjowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTQtMDUtMjlUMDA6MTY6\r\nMDhaIiBTZXNzaW9uSW5kZXg9Il8zMGE0YWY1MC1jODJiLTAxMzEtZjhiNS03\r\nODJiY2I1NmZjYWEiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNv\r\nbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6\r\nY2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvc2FtbDpBdXRo\r\nbkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpB\r\ndXRoblN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9u\r\nc2U+Cgo=\r\n' + }, + config: { + entryPoint: 'https://subspacesw1.okta.com/app/subspacesw_subspacetest_1/kvjj46lsDQEQYUDBZIYW/sso/saml', + cert: 'MIICoTCCAgqgAwIBAgIGAUY8zVPYMA0GCSqGSIb3DQEBBQUAMIGTMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFDASBgNVBAMMC3N1YnNwYWNlc3cxMRwwGgYJKoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMB4XDTE0MDUyNzA4MjkyN1oXDTQ0MDUyNzA4MzAyN1owgZMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKDARPa3RhMRQwEgYDVQQLDAtTU09Qcm92aWRlcjEUMBIGA1UEAwwLc3Vic3BhY2VzdzExHDAaBgkqhkiG9w0BCQEWDWluZm9Ab2t0YS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKYX/UdV9HLXbNKv0yc6YJBtzdWNuGTPIXWVPCb0OsRvR54lq+0SLPQGJS39flWKiqtUiEusuih6GdLfNvFAVLGPqBTWQ4Lc7r5+zD5qnH6LiTM1KCbaF3uu2P+T+utsGqFUZawA9FEVNLkICe36X1yE40X+/jMygyieQPMeJABvAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAHSWPG4y1bNyJySgbFqDcS/6MUNIrSfkQYIxzIGQ37qy5qAW15YkBUiW9TsGJRcrgusVrZIia18eYz84Zn7FyUZNMFo24L7l6BS0WCitIQScBCKB4QWA/b1szqs7EtpOcPP01TYNq2ras+VqR3aWAXLX2oKfClV/SUwszRf5U85Y=' + }, + expectedStatusCode: 500 + }, + { name: 'Onelogin -- valid config should succeed', + samlResponse: { + SAMLResponse: 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0\r\nYzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6\r\nbmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJSNjg5YjA3MzNiY2Nj\r\nYTIyYTEzN2UzNjU0ODMwMzEyMzMyOTQwYjFiZSIgVmVyc2lvbj0iMi4wIiBJ\r\nc3N1ZUluc3RhbnQ9IjIwMTQtMDUtMjhUMDA6MTY6MDhaIiBEZXN0aW5hdGlv\r\nbj0ie3JlY2lwaWVudH0iIEluUmVzcG9uc2VUbz0iX2E2ZmM0NmJlODRlMWUz\r\nY2YzYzUwIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29t\r\nL3NhbWwvbWV0YWRhdGEvMzcxNzU1PC9zYW1sOklzc3Vlcj48c2FtbHA6U3Rh\r\ndHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6\r\ndGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48\r\nc2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIw\r\nMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIw\r\nMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBWZXJzaW9uPSIyLjAiIElEPSJwZngz\r\nYjYzYzdiZS1mZTg2LTYyZmQtOGNiNS0xNmFiNjI3M2VmYWEiIElzc3VlSW5z\r\ndGFudD0iMjAxNC0wNS0yOFQwMDoxNjowOFoiPjxzYW1sOklzc3Vlcj5odHRw\r\nczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbC9tZXRhZGF0YS8zNzE3NTU8L3Nh\r\nbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cu\r\ndzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpD\r\nYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53\r\nMy5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1l\r\ndGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1s\r\nZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4M2I2M2M3\r\nYmUtZmU4Ni02MmZkLThjYjUtMTZhYjYyNzNlZmFhIj48ZHM6VHJhbnNmb3Jt\r\ncz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcv\r\nMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJh\r\nbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94\r\nbWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRo\r\nb2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRz\r\naWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5EQ25QVFFZQmIxaEtzcGJlNmZn\r\nMVUzcTh4bjQ9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2Rz\r\nOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPmUwK2FGb21BMCtKQVkw\r\nZjl0S3F6SXVxSVZTU3c3TGlGVXNuZUVES1BCV2RpVHoxc01kZ3IvMnkxZTkr\r\ncmphUzJtUm1DaS92U1FMWTN6VFl6MGhwNm5KTlUxOStUV29YbzlrSFF5V1Q0\r\nS2tlUUw0WHMvZ1ovQW9LQzIwaUhWS3RwUHBzMElRME1sL3FSb291U2l0dDZT\r\nZi9XRHoyTFYvcFdjSDJoeDV0djN4U3czNmhLMk5RYzdxdzdyMW1FWG52Y2pY\r\nUmVZbzhyclZmN1hIR0d4Tm9SSUVJQ1VJaTExMHV2c1dlbVNYZjBaMGR5YjBG\r\nVllPV3VTc1FNRGx6TnBoZUFEQmlmRk80VVRmU0VoRlp2bjhrVkNHWlVJd3Ji\r\nT2haMmQvK1lFdGd5dVRnK3F0c2xnZnk0ZHdkNFR2RWNmdVJ6UVRhemVlZnBy\r\nU0Z5aVFja0FYT2pjdz09PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5m\r\nbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRnpDQ0F2\r\nK2dBd0lCQWdJVUZKc1VqUE03QW1Xdk50RXZVTFNIbFRUTWlMUXdEUVlKS29a\r\nSWh2Y05BUUVGQlFBd1dERUxNQWtHQTFVRUJoTUNWVk14RVRBUEJnTlZCQW9N\r\nQ0ZOMVluTndZV05sTVJVd0V3WURWUVFMREF4UGJtVk1iMmRwYmlCSlpGQXhI\r\nekFkQmdOVkJBTU1Gazl1WlV4dloybHVJRUZqWTI5MWJuUWdOREl6TkRrd0ho\r\nY05NVFF3TlRFek1UZ3dOakV5V2hjTk1Ua3dOVEUwTVRnd05qRXlXakJZTVFz\r\nd0NRWURWUVFHRXdKVlV6RVJNQThHQTFVRUNnd0lVM1ZpYzNCaFkyVXhGVEFU\r\nQmdOVkJBc01ERTl1WlV4dloybHVJRWxrVURFZk1CMEdBMVVFQXd3V1QyNWxU\r\nRzluYVc0Z1FXTmpiM1Z1ZENBME1qTTBPVENDQVNJd0RRWUpLb1pJaHZjTkFR\r\nRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFLckF6SmRZOUZ6Rkx0NWJsQXJKZlB6\r\nZ2k4N0VuRkdsVGZjVjVUMVRVRHdMQmxEa1kvMFpHS25NT3BmM0Q3aWUyQzRw\r\nUEZPSW1Pb2djTTVrcERETDdxeFRYWjFld1hWeWpCZE11MjlORzJDNk56V2VR\r\nVFVNVWppMDFFY0hrQzhvK1B0czhBTmlOT1ljanhFZXloRXl6SktnRWl6YmxZ\r\nek1NS3pkck9FVDZRdXFXbzNDODNLKzUrNWRzakRuMW9vS0dSd2ozSHZnc1lj\r\nRnJRbDlOb2pnUUZqb29id3NpRS83QStPSmhMcEJjeS9uU1Znbm9KYU1mck8r\r\nSnNudWtaUHp0Ym50THZPbDU2K1ZyYTBOOG41TkFZaGFTYXlQaXYvYXloalZn\r\namZYZDF0ak1WVE9pRGtuVU93aXpadUoxWTNRSDk0dlV0QmdwMFdCcEJTcy94\r\nTXlUczhDQXdFQUFhT0IyRENCMVRBTUJnTlZIUk1CQWY4RUFqQUFNQjBHQTFV\r\nZERnUVdCQlJRTzRXcE01Zld3eGliNDlXVHVKa2ZZRGJ4T0RDQmxRWURWUjBq\r\nQklHTk1JR0tnQlJRTzRXcE01Zld3eGliNDlXVHVKa2ZZRGJ4T0tGY3BGb3dX\r\nREVMTUFrR0ExVUVCaE1DVlZNeEVUQVBCZ05WQkFvTUNGTjFZbk53WVdObE1S\r\nVXdFd1lEVlFRTERBeFBibVZNYjJkcGJpQkpaRkF4SHpBZEJnTlZCQU1NRms5\r\ndVpVeHZaMmx1SUVGalkyOTFiblFnTkRJek5EbUNGQlNiRkl6ek93SmxyemJS\r\nTDFDMGg1VTB6SWkwTUE0R0ExVWREd0VCL3dRRUF3SUhnREFOQmdrcWhraUc5\r\ndzBCQVFVRkFBT0NBUUVBQ2REQUFvYVpGQ0VZNXBtZndiS3VLclh0TzVpRThs\r\nV3RpQ1BqQ1pFVXVUNmJYUk5jcXJkbnVWL0VBZlg5V1FvWGphbFBpMGVNNzh6\r\nS21idlJHU1RVSHdXdzQ5UkhqRmZlSlVLdkhOZU5uRmdUWERqRVBOaE12aDY5\r\na0htNDUzbEZSbUIra2s2eWp0WFJaYVFFd1M4VXVvMk90K2tyZ05ibDZvVEJa\r\nSjBBSEgxTXRaRUNEbG9tczFLbTd6c0s4d0FpNWk4VFZJS2tWcjViMlZsaHJM\r\nZ0ZNdnpaNVZpQXhJTUdCNnc0N3lZNFFHUUIvNVE4eWE5aEJzOXZrbit3dWJB\r\nK3lyNGoxNEpYWjdibFZLRFNUWXZhNjVFYStQcUh5cnArV25tbmJ3Mk9iUzdp\r\nV2V4aVR5MWpEM0cwUjJhdkRCRmpNOEZqNURiZnVmc0UxYjBVMTBSVHRnPT08\r\nL2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5m\r\nbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBG\r\nb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9y\r\nbWF0OnRyYW5zaWVudCI+cGxvZXJAc3Vic3BhY2Vzdy5jb208L3NhbWw6TmFt\r\nZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2Fz\r\naXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0\r\nQ29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTQtMDUtMjhUMDA6\r\nMTk6MDhaIiBSZWNpcGllbnQ9IntyZWNpcGllbnR9IiBJblJlc3BvbnNlVG89\r\nIl9hNmZjNDZiZTg0ZTFlM2NmM2M1MCIvPjwvc2FtbDpTdWJqZWN0Q29uZmly\r\nbWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVm\r\nb3JlPSIyMDE0LTA1LTI4VDAwOjEzOjA4WiIgTm90T25PckFmdGVyPSIyMDE0\r\nLTA1LTI4VDAwOjE5OjA4WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48\r\nc2FtbDpBdWRpZW5jZT57YXVkaWVuY2V9PC9zYW1sOkF1ZGllbmNlPjwvc2Ft\r\nbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1s\r\nOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wNS0yOFQwMDox\r\nNjowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTQtMDUtMjlUMDA6MTY6\r\nMDhaIiBTZXNzaW9uSW5kZXg9Il8zMGE0YWY1MC1jODJiLTAxMzEtZjhiNS03\r\nODJiY2I1NmZjYWEiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNv\r\nbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6\r\nY2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvc2FtbDpBdXRo\r\nbkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpB\r\ndXRoblN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9u\r\nc2U+Cgo=\r\n' + }, + config: { + entryPoint: 'https://app.onelogin.com/trust/saml2/http-post/sso/371755', + cert: 'MIIEFzCCAv+gAwIBAgIUFJsUjPM7AmWvNtEvULSHlTTMiLQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UEBhMCVVMxETAPBgNVBAoMCFN1YnNwYWNlMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgNDIzNDkwHhcNMTQwNTEzMTgwNjEyWhcNMTkwNTE0MTgwNjEyWjBYMQswCQYDVQQGEwJVUzERMA8GA1UECgwIU3Vic3BhY2UxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA0MjM0OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKrAzJdY9FzFLt5blArJfPzgi87EnFGlTfcV5T1TUDwLBlDkY/0ZGKnMOpf3D7ie2C4pPFOImOogcM5kpDDL7qxTXZ1ewXVyjBdMu29NG2C6NzWeQTUMUji01EcHkC8o+Pts8ANiNOYcjxEeyhEyzJKgEizblYzMMKzdrOET6QuqWo3C83K+5+5dsjDn1ooKGRwj3HvgsYcFrQl9NojgQFjoobwsiE/7A+OJhLpBcy/nSVgnoJaMfrO+JsnukZPztbntLvOl56+Vra0N8n5NAYhaSayPiv/ayhjVgjfXd1tjMVTOiDknUOwizZuJ1Y3QH94vUtBgp0WBpBSs/xMyTs8CAwEAAaOB2DCB1TAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRQO4WpM5fWwxib49WTuJkfYDbxODCBlQYDVR0jBIGNMIGKgBRQO4WpM5fWwxib49WTuJkfYDbxOKFcpFowWDELMAkGA1UEBhMCVVMxETAPBgNVBAoMCFN1YnNwYWNlMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgNDIzNDmCFBSbFIzzOwJlrzbRL1C0h5U0zIi0MA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEACdDAAoaZFCEY5pmfwbKuKrXtO5iE8lWtiCPjCZEUuT6bXRNcqrdnuV/EAfX9WQoXjalPi0eM78zKmbvRGSTUHwWw49RHjFfeJUKvHNeNnFgTXDjEPNhMvh69kHm453lFRmB+kk6yjtXRZaQEwS8Uuo2Ot+krgNbl6oTBZJ0AHH1MtZECDloms1Km7zsK8wAi5i8TVIKkVr5b2VlhrLgFMvzZ5ViAxIMGB6w47yY4QGQB/5Q8ya9hBs9vkn+wubA+yr4j14JXZ7blVKDSTYva65Ea+PqHyrp+Wnmnbw2ObS7iWexiTy1jD3G0R2avDBFjM8Fj5DbfufsE1b0U10RTtg==' + }, + expectedStatusCode: 200, + expectedNameIDStartsWith: 'ploer' + }, + ]; + + var server; + + function testForCheck( check ) { + return function( done ) { + var app = express(); + app.use( bodyParser.urlencoded() ); + app.use( passport.initialize() ); + var config = check.config; + config.callbackUrl = 'http://localhost:3033/login'; + var profile = null; + passport.use( new SamlStrategy( config, function(_profile, done) { + profile = _profile; + done(null, { id: profile.nameID } ); + }) + ); + + app.post( '/login', + passport.authenticate( "saml", { session: false } ), + function(req, res) { + res.send( 200, "200 OK" ); + }); + + app.use( function( err, req, res, next ) { + //console.log( err.stack ); + res.send( 500, '500 Internal Server Error' ); + }); + + server = app.listen( 3033, function() { + var requestOpts = { + url: 'http://localhost:3033/login', + method: 'POST', + form: check.samlResponse + }; + + request(requestOpts, function(err, response, body) { + should.not.exist(err); + response.statusCode.should.equal(check.expectedStatusCode); + if (response.statusCode == 200) + profile.nameID.should.startWith(check.expectedNameIDStartsWith); + done(); + }); + }); + }; + } + + for( var i = 0; i < capturedChecks.length; i++ ) { + var check = capturedChecks[i]; + it( check.name, testForCheck( check ) ); + } + + afterEach( function( done ) { + server.close( done ); + }); + }); +}); From b4c0261266bd247a2b9ceb581b477d8e0447021c Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 15:14:47 -0700 Subject: [PATCH 27/43] Update dependency versions. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f1d6a86b..f1de117c 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ ], "main": "./lib/passport-saml", "dependencies": { - "passport": "0.1.x", - "xml2js": "0.2.0", + "passport": "0.2.x", + "xml2js": "0.4.0", "xml-crypto": "0.0.x", "xmldom": "0.1.x" }, From d7d284246bebbe69bd09201358adc5a58135c6e5 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 15:59:42 -0700 Subject: [PATCH 28/43] Updating xml2js to latest. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f1de117c..2fcaa833 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "main": "./lib/passport-saml", "dependencies": { "passport": "0.2.x", - "xml2js": "0.4.0", + "xml2js": "0.4.x", "xml-crypto": "0.0.x", "xmldom": "0.1.x" }, From 8cd061a24b021477ca61d6254149cbd0a504cc3f Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 16:00:49 -0700 Subject: [PATCH 29/43] Cleaning up jshint warning. --- lib/passport-saml/saml.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index b464494f..a0ad5d60 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -306,6 +306,10 @@ function validateResponse(self, doc, callback) { if (attributeStatement) { var attributes = self.getElement(attributeStatement[0], 'Attribute'); + var attrValueMapper = function(value) { + return typeof value === 'string' ? value : value._; + }; + if (attributes) { attributes.forEach(function (attribute) { var value = self.getElement(attribute, 'AttributeValue'); @@ -316,10 +320,6 @@ function validateResponse(self, doc, callback) { } }); } - - function attrValueMapper(value) { - return typeof value === 'string' ? value : value._; - } } From 1e0bd33ba89543e7037df3596c2fb0048447aacf Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 16:13:52 -0700 Subject: [PATCH 30/43] Take advantage of xml2js tagNameProcessors to simplify code. --- lib/passport-saml/saml.js | 47 +++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index a0ad5d60..58b53d44 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -203,17 +203,6 @@ SAML.prototype.validateSignature = function (xml, cert, signature) { return sig.checkSignature(xml); }; -SAML.prototype.getElement = function (parentElement, elementName) { - // once using xml2js 0.4.1+, can just use a tag name processor - var namespacePrefixesToCheck = ['saml', 'samlp', 'saml2', 'saml2p']; - for(var i=0; i < namespacePrefixesToCheck.length; i++ ) { - var potentialElement = parentElement[namespacePrefixesToCheck[i] + ':' + elementName]; - if ( potentialElement ) - return potentialElement; - } - return parentElement[elementName]; -}; - SAML.prototype.validatePostResponse = function (container, callback) { var xml = new Buffer(container.SAMLResponse, 'base64').toString('ascii'); return this.validateXML(xml, null, validateResponse, callback); @@ -251,7 +240,11 @@ SAML.prototype.validateRedirect = function(data, signature, validate, callback) SAML.prototype.validateXML = function (xml, signature, validate, callback) { var self = this; - var parser = new xml2js.Parser({explicitRoot:true}); + var parserConfig = { + explicitRoot: true, + tagNameProcessors: [xml2js.processors.stripPrefix] + }; + var parser = new xml2js.Parser(parserConfig); parser.parseString(xml, function (err, doc) { if (err) { return callback(err, null, false); @@ -267,15 +260,15 @@ SAML.prototype.validateXML = function (xml, signature, validate, callback) { }; function validateResponse(self, doc, callback) { - var response = self.getElement(doc, 'Response'); + var response = doc['Response']; if (response) { - var assertion = self.getElement(response, 'Assertion'); + var assertion = response['Assertion']; if (!assertion) { - var status = self.getElement(response, 'Status'); + var status = response['Status']; if (status) { - status = self.getElement(status[0], 'StatusCode'); + status = status[0]['StatusCode']; if (status && status[0].$.Value === "urn:oasis:names:tc:SAML:2.0:status:Responder") { - status = self.getElement(status[0], 'StatusCode'); + status = status[0]['StatusCode']; if (status && status[0].$.Value === "urn:oasis:names:tc:SAML:2.0:status:NoPassive") { return callback(null, null, false); } @@ -285,14 +278,14 @@ function validateResponse(self, doc, callback) { } var profile = {}; - var issuer = self.getElement(assertion[0], 'Issuer'); + var issuer = assertion[0]['Issuer']; if (issuer) { profile.issuer = issuer[0]; } - var subject = self.getElement(assertion[0], 'Subject'); + var subject = assertion[0]['Subject']; if (subject) { - var nameID = self.getElement(subject[0], 'NameID'); + var nameID = subject[0]['NameID']; if (nameID) { profile.nameID = nameID[0]._; @@ -302,9 +295,9 @@ function validateResponse(self, doc, callback) { } } - var attributeStatement = self.getElement(assertion[0], 'AttributeStatement'); + var attributeStatement = assertion[0]['AttributeStatement']; if (attributeStatement) { - var attributes = self.getElement(attributeStatement[0], 'Attribute'); + var attributes = attributeStatement[0]['Attribute']; var attrValueMapper = function(value) { return typeof value === 'string' ? value : value._; @@ -312,7 +305,7 @@ function validateResponse(self, doc, callback) { if (attributes) { attributes.forEach(function (attribute) { - var value = self.getElement(attribute, 'AttributeValue'); + var value = attribute['AttributeValue']; if (value.length === 1) { profile[attribute.$.Name] = attrValueMapper(value[0]); } else { @@ -334,7 +327,7 @@ function validateResponse(self, doc, callback) { callback(null, profile, false); } else { - var logoutResponse = self.getElement(doc, 'LogoutResponse'); + var logoutResponse = doc['LogoutResponse']; if (logoutResponse){ callback(null, null, true); @@ -346,7 +339,7 @@ function validateResponse(self, doc, callback) { } function validateRequest(self, doc, callback) { - var request = self.getElement(doc, 'LogoutRequest'); + var request = doc['LogoutRequest']; if (request) { var profile = {}; if (request.$.ID) { @@ -354,14 +347,14 @@ function validateRequest(self, doc, callback) { } else { return callback(new Error('Missing SAML LogoutRequest ID'), null, false); } - var issuer = self.getElement(request, 'Issuer'); + var issuer = request['Issuer']; if (issuer) { profile.issuer = issuer[0]; } else { return callback(new Error('Missing SAML issuer'), null, false); } - var nameID = self.getElement(request, 'NameID'); + var nameID = request['NameID']; if (nameID) { profile.nameID = nameID[0]._; if (nameID[0].$.Format) { From f96ce0d308afef685aff13b2de9179b6e19eba74 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 17:17:15 -0700 Subject: [PATCH 31/43] Expand tests to cover requests. --- test/tests.js | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/test/tests.js b/test/tests.js index a0ed373c..c4da41c3 100644 --- a/test/tests.js +++ b/test/tests.js @@ -5,6 +5,9 @@ var passport = require( 'passport' ); var SamlStrategy = require( '../lib/passport-saml/index.js' ).Strategy; var request = require( 'request' ); var should = require( 'should' ); +var zlib = require( 'zlib' ); +var querystring = require( 'querystring' ); +var parseString = require( 'xml2js' ).parseString; describe( 'passport-saml /', function() { describe( 'captured saml responses /', function() { @@ -98,4 +101,131 @@ describe( 'passport-saml /', function() { server.close( done ); }); }); + + describe( 'captured SAML requests /', function() { + var capturedChecks = [ + { name: "Empty Config", + config: {}, + result: { + 'samlp:AuthnRequest': + { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + Version: '2.0', + ProtocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + AssertionConsumerServiceURL: 'http://localhost:3033/login', + Destination: 'https://wwwexampleIdp.com/saml' }, + 'saml:Issuer': + [ { _: 'onelogin_saml', + '$': { 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion' } } ], + 'samlp:NameIDPolicy': + [ { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + Format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', + AllowCreate: 'true' } } ], + 'samlp:RequestedAuthnContext': + [ { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + Comparison: 'exact' }, + 'saml:AuthnContextClassRef': + [ { _: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', + '$': { 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion' } } ] } ] } } + }, + { name: "Config #2", + config: { + issuer: 'http://exampleSp.com/saml', + identifierFormat: 'alternateIdentifier', + passive: true + }, + result: { + 'samlp:AuthnRequest': + { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + Version: '2.0', + ProtocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + AssertionConsumerServiceURL: 'http://localhost:3033/login', + Destination: 'https://wwwexampleIdp.com/saml', + IsPassive: 'true' }, + 'saml:Issuer': + [ { _: 'http://exampleSp.com/saml', + '$': { 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion' } } ], + 'samlp:NameIDPolicy': + [ { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + Format: 'alternateIdentifier', + AllowCreate: 'true' } } ], + 'samlp:RequestedAuthnContext': + [ { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + Comparison: 'exact' }, + 'saml:AuthnContextClassRef': + [ { _: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', + '$': { 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion' } } ] } ] } } + } + ]; + + var server; + + function testForCheck( check ) { + return function( done ) { + var app = express(); + app.use( bodyParser.urlencoded() ); + app.use( passport.initialize() ); + var config = check.config; + config.callbackUrl = 'http://localhost:3033/login'; + config.entryPoint = 'https://wwwexampleIdp.com/saml'; + var profile = null; + passport.use( new SamlStrategy( config, function(_profile, done) { + profile = _profile; + done(null, { id: profile.nameID } ); + }) + ); + + app.get( '/login', + passport.authenticate( "saml", { samlFallback: 'login-request', session: false } ), + function(req, res) { + res.send( 200, "200 OK" ); + }); + + app.use( function( err, req, res, next ) { + //console.log( err.stack ); + res.send( 500, '500 Internal Server Error' ); + }); + + server = app.listen( 3033, function() { + var requestOpts = { + url: 'http://localhost:3033/login', + method: 'get', + followRedirect: false + }; + + request(requestOpts, function(err, response, body) { + should.not.exist(err); + response.statusCode.should.equal(302); + var query = response.headers.location.match( /^[^\?]*\?(.*)$/ )[1]; + var encodedSamlRequest = querystring.parse( query ).SAMLRequest; + var buffer = new Buffer(encodedSamlRequest, 'base64') + zlib.inflateRaw( buffer, function(err, samlRequest) { + should.not.exist( err ); + parseString( samlRequest.toString(), function( err, doc ) { + should.not.exist( err ); + delete doc['samlp:AuthnRequest']['$']["ID"]; + delete doc['samlp:AuthnRequest']['$']["IssueInstant"]; + doc.should.eql( check.result ); + done(); + }); + }); + }); + }); + }; + } + + for( var i = 0; i < capturedChecks.length; i++ ) { + var check = capturedChecks[i]; + it( check.name, testForCheck( check ) ); + } + + afterEach( function( done ) { + server.close( done ); + }); + }); }); From 4f4beddef4e4cbc3d922615c1b0de4eaf4c72185 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 20:19:57 -0700 Subject: [PATCH 32/43] Add some tests for generateLogoutRequest and generateLogoutResponse functions. --- test/tests.js | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/tests.js b/test/tests.js index c4da41c3..628f3302 100644 --- a/test/tests.js +++ b/test/tests.js @@ -8,6 +8,7 @@ var should = require( 'should' ); var zlib = require( 'zlib' ); var querystring = require( 'querystring' ); var parseString = require( 'xml2js' ).parseString; +var SAML = require( '../lib/passport-saml/saml.js' ).SAML; describe( 'passport-saml /', function() { describe( 'captured saml responses /', function() { @@ -228,4 +229,59 @@ describe( 'passport-saml /', function() { server.close( done ); }); }); + + describe( 'saml.js / ', function() { + it( 'generateLogoutRequest', function( done ) { + var expectedRequest = { + 'samlp:LogoutRequest': + { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion', + //ID: '_85ba0a112df1ffb57805', + Version: '2.0', + //IssueInstant: '2014-05-29T03:32:23Z', + Destination: 'foo' }, + 'saml:Issuer': + [ { _: 'onelogin_saml', + '$': { 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion' } } ], + 'saml:NameID': [ { _: 'bar', '$': { Format: 'foo' } } ] } }; + + var samlObj = new SAML( { entryPoint: "foo" } ); + var logoutRequest = samlObj.generateLogoutRequest({ + user: { + nameIDFormat: 'foo', + nameID: 'bar' + } + }); + parseString( logoutRequest, function( err, doc ) { + delete doc['samlp:LogoutRequest']['$']["ID"]; + delete doc['samlp:LogoutRequest']['$']["IssueInstant"]; + doc.should.eql( expectedRequest ); + done(); + }); + }); + + it( 'generateLogoutResponse', function( done ) { + var expectedResponse = { + 'samlp:LogoutResponse': + { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion', + //ID: '_d11b3c5e085b2417f4aa', + Version: '2.0', + //IssueInstant: '2014-05-29T01:11:32Z', + InResponseTo: 'quux' }, + 'saml:Issuer': [ 'onelogin_saml' ], + 'samlp:Status': [ { 'samlp:StatusCode': [ { '$': { Value: 'urn:oasis:names:tc:SAML:2.0:status:Success' } } ] } ] } }; + + var samlObj = new SAML( {} ); + var logoutRequest = samlObj.generateLogoutResponse({}, { ID: "quux" }); + parseString( logoutRequest, function( err, doc ) { + delete doc['samlp:LogoutResponse']['$']["ID"]; + delete doc['samlp:LogoutResponse']['$']["IssueInstant"]; + doc.should.eql( expectedResponse ); + done(); + }); + }); + }); }); From a11d76ac5c24b55dc669464a01a8c90f59cf17d0 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 20:46:33 -0700 Subject: [PATCH 33/43] Use xmlbuilder to construct xml documents. --- lib/passport-saml/saml.js | 114 +++++++++++++++++++++++++++----------- package.json | 3 +- 2 files changed, 83 insertions(+), 34 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 58b53d44..8fc22832 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -4,6 +4,7 @@ var xmlCrypto = require('xml-crypto'); var crypto = require('crypto'); var xmldom = require('xmldom'); var querystring = require('querystring'); +var xmlbuilder = require('xmlbuilder'); var SAML = function (options) { this.options = this.initialize(options); @@ -65,42 +66,68 @@ SAML.prototype.generateAuthorizeRequest = function (req, isPassive) { callbackUrl = this.options.protocol + req.headers.host + this.options.path; } - var request = - "" + - "" + this.options.issuer + "\n"; + var request = { + 'samlp:AuthnRequest': { + '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + '@ID': id, + '@Version': '2.0', + '@IssueInstant': instant, + '@ProtocolBinding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + '@AssertionConsumerServiceURL': callbackUrl, + '@Destination': this.options.entryPoint, + 'saml:Issuer' : { + '@xmlns:saml' : 'urn:oasis:names:tc:SAML:2.0:assertion', + '#text': this.options.issuer + }, + 'samlp:RequestedAuthnContext' : { + '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + '@Comparison': 'exact', + 'saml:AuthnContextClassRef': { + '@xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion', + '#text': 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + } + } + } + }; + + if (isPassive) + request['samlp:AuthnRequest']['@IsPassive'] = true; if (this.options.identifierFormat) { - request += "\n"; + request['samlp:AuthnRequest']['samlp:NameIDPolicy'] = { + '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + '@Format': this.options.identifierFormat, + '@AllowCreate': 'true' + }; } - request += - "" + - "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport\n" + - ""; - - return request; + return xmlbuilder.create(request).end(); }; SAML.prototype.generateLogoutRequest = function (req) { var id = "_" + this.generateUniqueID(); var instant = this.generateInstant(); - //samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" - // ID="_135ad2fd-b275-4428-b5d6-3ac3361c3a7f" Version="2.0" Destination="https://idphost/adfs/ls/" - //IssueInstant="2008-06-03T12:59:57Z">myhostmyemail@mydomain.com_0628125f-7f95-42cc-ad8e-fde86ae90bbe - // - - var request = "" + - "" + this.options.issuer + ""+ - ""+req.user.nameID+""+ - ""; - return request; + var request = { + 'samlp:LogoutRequest' : { + '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + '@xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion', + '@ID': id, + '@Version': '2.0', + '@IssueInstant': instant, + '@Destination': this.options.entryPoint, + 'saml:Issuer' : { + '@xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion', + '#text': this.options.issuer + }, + 'saml:NameID' : { + '@Format': req.user.nameIDFormat, + '#text': req.user.nameID + } + } + }; + + return xmlbuilder.create(request).end(); }; SAML.prototype.generateLogoutResponse = function (req, logoutRequest) { @@ -114,13 +141,34 @@ SAML.prototype.generateLogoutResponse = function (req, logoutRequest) { // // - var request = "" + - "" + this.options.issuer + ""+ - ""+ - ""; - return request; + // var request = "" + + // "" + this.options.issuer + ""+ + // ""+ + // ""; + // return request; + + var request = { + 'samlp:LogoutResponse' : { + '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + '@xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion', + '@ID': id, + '@Version': '2.0', + '@IssueInstant': instant, + '@InResponseTo': logoutRequest.ID, + 'saml:Issuer' : { + '#text': this.options.issuer + }, + 'samlp:Status': { + 'samlp:StatusCode': { + '@Value': 'urn:oasis:names:tc:SAML:2.0:status:Success' + } + } + } + }; + + return xmlbuilder.create(request).end(); }; SAML.prototype.requestToUrl = function (request, response, operation, additionalParameters, callback) { diff --git a/package.json b/package.json index 2fcaa833..d9cb43fd 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "passport": "0.2.x", "xml2js": "0.4.x", "xml-crypto": "0.0.x", - "xmldom": "0.1.x" + "xmldom": "0.1.x", + "xmlbuilder": "~2.2" }, "devDependencies": { "express": "4.x", From 67df1baf2885e10a59dde5ba32fe269f8077221b Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 20:58:52 -0700 Subject: [PATCH 34/43] Updating minimum engine version to 0.8, and updating travis CI engine versions. --- .travis.yml | 6 ++++-- package.json | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 76fa21e3..a4899e1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: node_js node_js: - - 0.6 - - 0.8 + - "0.6" + - "0.8" + - "0.10" + - "0.11" script: ./node_modules/.bin/jshint lib diff --git a/package.json b/package.json index d9cb43fd..94f15dd6 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "should": "*" }, "engines": { - "node": ">= 0.6.0" + "node": ">= 0.8.0" }, "scripts" : { "test": "mocha" From 7c9d197d93df38feed3706175b929d2aecb7f2cf Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 21:01:18 -0700 Subject: [PATCH 35/43] Remove some commented out code that got missed. --- lib/passport-saml/saml.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 8fc22832..2bd59e84 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -134,21 +134,6 @@ SAML.prototype.generateLogoutResponse = function (req, logoutRequest) { var id = "_" + this.generateUniqueID(); var instant = this.generateInstant(); -// -// https://sts.windows.net/82869000-6ad1-48f0-8171-272ed18796e9/ -// -// -// -// - - // var request = "" + - // "" + this.options.issuer + ""+ - // ""+ - // ""; - // return request; - var request = { 'samlp:LogoutResponse' : { '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', From 5b90b1dba4d19635dfd3fdb1f7ecbb01821fc4e2 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 21:07:10 -0700 Subject: [PATCH 36/43] Jshint cleaning. --- lib/passport-saml/saml.js | 30 +++++++++++++++--------------- package.json | 3 ++- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 2bd59e84..0d1525b5 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -293,15 +293,15 @@ SAML.prototype.validateXML = function (xml, signature, validate, callback) { }; function validateResponse(self, doc, callback) { - var response = doc['Response']; + var response = doc.Response; if (response) { - var assertion = response['Assertion']; + var assertion = response.Assertion; if (!assertion) { - var status = response['Status']; + var status = response.Status; if (status) { - status = status[0]['StatusCode']; + status = status[0].StatusCode; if (status && status[0].$.Value === "urn:oasis:names:tc:SAML:2.0:status:Responder") { - status = status[0]['StatusCode']; + status = status[0].StatusCode; if (status && status[0].$.Value === "urn:oasis:names:tc:SAML:2.0:status:NoPassive") { return callback(null, null, false); } @@ -311,14 +311,14 @@ function validateResponse(self, doc, callback) { } var profile = {}; - var issuer = assertion[0]['Issuer']; + var issuer = assertion[0].Issuer; if (issuer) { profile.issuer = issuer[0]; } - var subject = assertion[0]['Subject']; + var subject = assertion[0].Subject; if (subject) { - var nameID = subject[0]['NameID']; + var nameID = subject[0].NameID; if (nameID) { profile.nameID = nameID[0]._; @@ -328,9 +328,9 @@ function validateResponse(self, doc, callback) { } } - var attributeStatement = assertion[0]['AttributeStatement']; + var attributeStatement = assertion[0].AttributeStatement; if (attributeStatement) { - var attributes = attributeStatement[0]['Attribute']; + var attributes = attributeStatement[0].Attribute; var attrValueMapper = function(value) { return typeof value === 'string' ? value : value._; @@ -338,7 +338,7 @@ function validateResponse(self, doc, callback) { if (attributes) { attributes.forEach(function (attribute) { - var value = attribute['AttributeValue']; + var value = attribute.AttributeValue; if (value.length === 1) { profile[attribute.$.Name] = attrValueMapper(value[0]); } else { @@ -360,7 +360,7 @@ function validateResponse(self, doc, callback) { callback(null, profile, false); } else { - var logoutResponse = doc['LogoutResponse']; + var logoutResponse = doc.LogoutResponse; if (logoutResponse){ callback(null, null, true); @@ -372,7 +372,7 @@ function validateResponse(self, doc, callback) { } function validateRequest(self, doc, callback) { - var request = doc['LogoutRequest']; + var request = doc.LogoutRequest; if (request) { var profile = {}; if (request.$.ID) { @@ -380,14 +380,14 @@ function validateRequest(self, doc, callback) { } else { return callback(new Error('Missing SAML LogoutRequest ID'), null, false); } - var issuer = request['Issuer']; + var issuer = request.Issuer; if (issuer) { profile.issuer = issuer[0]; } else { return callback(new Error('Missing SAML issuer'), null, false); } - var nameID = request['NameID']; + var nameID = request.NameID; if (nameID) { profile.nameID = nameID[0]._; if (nameID[0].$.Format) { diff --git a/package.json b/package.json index 94f15dd6..ec56db1c 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "node": ">= 0.8.0" }, "scripts" : { - "test": "mocha" + "test": "mocha", + "jshint": "./node_modules/.bin/jshint lib" } } From c331df8cff8800ed3e29229d2b44f701956db640 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 21:12:21 -0700 Subject: [PATCH 37/43] Travis config: drop 0.6 engine; run mocha. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a4899e1d..b713553e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,9 @@ language: node_js node_js: - - "0.6" - "0.8" - "0.10" - "0.11" -script: ./node_modules/.bin/jshint lib +script: + - npm test + - ./node_modules/.bin/jshint lib From 73712f6519d626d4bb1371aed5714efb4589334e Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 21:23:38 -0700 Subject: [PATCH 38/43] Including mocha as devDependency, and rolling back express to 3.x to see if that helps 0.8 build. --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ec56db1c..915cab30 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,12 @@ "xmlbuilder": "~2.2" }, "devDependencies": { - "express": "4.x", + "express": "3.x", "ejs": "0.7.x", "jshint": "*", "request": "*", - "should": "*" + "should": "*", + "mocha": "*" }, "engines": { "node": ">= 0.8.0" From 0f2eb7af0d421996fc3e41a19e5bd301e98b5a3e Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 21:30:05 -0700 Subject: [PATCH 39/43] More travis build fixing. --- .travis.yml | 3 +++ package.json | 1 + 2 files changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index b713553e..ea3717bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,9 @@ node_js: - "0.10" - "0.11" +before_install: + - npm install -g npm + script: - npm test - ./node_modules/.bin/jshint lib diff --git a/package.json b/package.json index 915cab30..3ac0fd07 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "devDependencies": { "express": "3.x", + "body-parser": "*", "ejs": "0.7.x", "jshint": "*", "request": "*", From aa674b33f9b180c8418bb084a2d9776859d7b639 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 21:32:42 -0700 Subject: [PATCH 40/43] Reverting npm name to 'passport-saml' & bumping version. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3ac0fd07..9a5839fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "passport-saml-too", - "version": "0.0.6", + "name": "passport-saml", + "version": "0.0.7", "licenses": [{ "type": "MIT", "url": "https://github.com/herby/passport-saml/raw/master/LICENSE" From 6da8caae2f23a140841f26f15cad7d97d889ab5c Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 21:35:31 -0700 Subject: [PATCH 41/43] Updating license link & contributors list. --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9a5839fc..f0c5662f 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.7", "licenses": [{ "type": "MIT", - "url": "https://github.com/herby/passport-saml/raw/master/LICENSE" + "url": "https://github.com/bergie/passport-saml/raw/master/LICENSE" }], "keywords": ["saml", "adfs", "sso", "shibboleth"], "description": "SAML 2.0 authentication strategy for Passport", @@ -14,7 +14,8 @@ }, "contributors" : [ "Michael Bosworth", - "Herbert Vojčík" + "Herbert Vojčík", + "Peter Loer" ], "main": "./lib/passport-saml", "dependencies": { From 6f2087e2aa8f14953ec454e82e9882e28ba90da3 Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Wed, 28 May 2014 21:52:29 -0700 Subject: [PATCH 42/43] Remove redirect support from passport-saml-too branch, since it doesn't validate signatures. --- lib/passport-saml/saml.js | 38 ++++++----------------------------- lib/passport-saml/strategy.js | 6 +----- 2 files changed, 7 insertions(+), 37 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 0d1525b5..71624e77 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -218,11 +218,10 @@ SAML.prototype.certToPEM = function (cert) { return cert; }; -SAML.prototype.validateSignature = function (xml, cert, signature) { +SAML.prototype.validateSignature = function (xml, cert) { var self = this; var doc = new xmldom.DOMParser().parseFromString(xml); - if (signature === "") { return true; } - signature = signature || xmlCrypto.xpath(doc, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0].toString(); + var signature = xmlCrypto.xpath(doc, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0].toString(); var sig = new xmlCrypto.SignedXml(); sig.keyInfoProvider = { getKeyInfo: function (key) { @@ -238,40 +237,15 @@ SAML.prototype.validateSignature = function (xml, cert, signature) { SAML.prototype.validatePostResponse = function (container, callback) { var xml = new Buffer(container.SAMLResponse, 'base64').toString('ascii'); - return this.validateXML(xml, null, validateResponse, callback); + return this.validateXML(xml, validateResponse, callback); }; SAML.prototype.validatePostRequest = function (container, callback) { var xml = new Buffer(container.SAMLRequest, 'base64').toString('ascii'); - return this.validateXML(xml, null, validateRequest, callback); + return this.validateXML(xml, validateRequest, callback); }; -SAML.prototype.validateRedirectResponse = function (container, callback) { - var data = new Buffer(container.SAMLResponse, "base64"); - var signature = null; //new Buffer(container.Signature, 'base64').toString('ascii'); - this.validateRedirect(data, signature, validateResponse, callback); -}; - -SAML.prototype.validateRedirectRequest = function (container, callback) { - var data = new Buffer(container.SAMLRequest, "base64"); - var signature = null; //new Buffer(container.Signature, 'base64').toString('ascii'); - this.validateRedirect(data, signature, validateRequest, callback); -}; - -SAML.prototype.validateRedirect = function(data, signature, validate, callback) { - var self = this; - // TODO verify redirect - - zlib.inflateRaw(data, function(err, inflated) { - if (err) { - return callback(err); - } - - self.validateXML(inflated.toString("utf8"), "", validate, callback); - }); -}; - -SAML.prototype.validateXML = function (xml, signature, validate, callback) { +SAML.prototype.validateXML = function (xml, validate, callback) { var self = this; var parserConfig = { explicitRoot: true, @@ -284,7 +258,7 @@ SAML.prototype.validateXML = function (xml, signature, validate, callback) { } // Verify signature - if (self.options.cert && !self.validateSignature(xml, self.options.cert, signature)) { + if (self.options.cert && !self.validateSignature(xml, self.options.cert)) { return callback(new Error('Invalid signature'), null, false); } diff --git a/lib/passport-saml/strategy.js b/lib/passport-saml/strategy.js index 7cbd7dd6..6e8a0813 100644 --- a/lib/passport-saml/strategy.js +++ b/lib/passport-saml/strategy.js @@ -62,12 +62,8 @@ Strategy.prototype.authenticate = function (req, options) { } } - if (req.query && req.query.SAMLResponse) { - this._saml.validateRedirectResponse(req.query, validateCallback); - } else if (req.body && req.body.SAMLResponse) { + if (req.body && req.body.SAMLResponse) { this._saml.validatePostResponse(req.body, validateCallback); - } else if (req.query && req.query.SAMLRequest) { - this._saml.validateRedirectRequest(req.query, validateCallback); } else if (req.body && req.body.SAMLRequest) { this._saml.validatePostRequest(req.body, validateCallback); } else if (options.samlFallback) { From 8817db5a9e67985e025d6f70bc4654d9b6964bda Mon Sep 17 00:00:00 2001 From: Peter Loer Date: Thu, 29 May 2014 09:38:45 -0700 Subject: [PATCH 43/43] Add repository tag to package.json. --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index f0c5662f..0f2bf20d 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,10 @@ "Herbert Vojčík", "Peter Loer" ], + "repository" : { + "type" : "git", + "url" : "https://github.com/bergie/passport-saml.git" + }, "main": "./lib/passport-saml", "dependencies": { "passport": "0.2.x",