diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index dac0020d..7873023a 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -721,7 +721,14 @@ SAML.prototype.validatePostResponse = function (container, callback) { }) .fail(function(err) { debug('validatePostResponse resulted in an error: %s', err); - callback(err); + if (self.options.validateInResponseTo) { + Q.ninvoke(self.cacheProvider, 'remove', inResponseTo) + .then(function() { + callback(err); + }); + } else { + callback(err); + } }) .done(); }; diff --git a/test/tests.js b/test/tests.js index 5c35abd2..6bbe761d 100644 --- a/test/tests.js +++ b/test/tests.js @@ -535,13 +535,13 @@ describe( 'passport-saml /', function() { var query = response.headers.location.match( /^[^\?]*\?(.*)$/ )[1]; encodedSamlRequest = querystring.parse( query ).SAMLRequest; } - + var buffer = Buffer.from(encodedSamlRequest, 'base64'); if (check.config.skipRequestCompression) helper(null, buffer); else zlib.inflateRaw( buffer, helper ); - + function helper(err, samlRequest) { try { should.not.exist( err ); @@ -772,7 +772,7 @@ describe( 'passport-saml /', function() { try { var id = doc['samlp:LogoutRequest']['$']["ID"]; var issueInstant = doc['samlp:LogoutRequest']['$']["IssueInstant"]; - + id.should.be.an.instanceOf(String); issueInstant.should.be.an.instanceOf(String); cacheSaveSpy.called.should.eql(true); @@ -968,9 +968,9 @@ describe( 'passport-saml /', function() { should(profile).have.property("evil-corp.egroupid").eql("vincent.vega@evil-corp.com"); // attributes without attributeValue child should be ignored should(profile).not.have.property("evilcorp.roles"); - + fakeClock.restore(); - + done(); } catch (err2) { done(err2); @@ -978,6 +978,42 @@ describe( 'passport-saml /', function() { }); }); + it('removes InResponseTo value if response validation fails', function(done) { + var requestId = '_a6fc46be84e1e3cf3c50'; + var xml = 'https://app.onelogin.com/saml/metadata/371755' + + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw=='+TEST_CERT+'ben@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + + ''; + var base64xml = Buffer.from( xml ).toString('base64'); + var container = { SAMLResponse: base64xml }; + var samlConfig = { + entryPoint: 'https://app.onelogin.com/trust/saml2/http-post/sso/371755', + cert: TEST_CERT, + validateInResponseTo: true + }; + var samlObj = new SAML( samlConfig ); + + // Mock the SAML request being passed through Passport-SAML + samlObj.cacheProvider.save(requestId, new Date().toISOString(), function(){}); + + samlObj.validatePostResponse( container, function( err, profile, logout ) { + try { + should.exist(err); + err.message.should.match("Invalid signature"); + } catch (err2) { + done(err2); + } + samlObj.validatePostResponse( container, function( err, profile, logout ) { + try { + should.exist(err); + err.message.should.match("InResponseTo is not valid"); + done(); + } catch (err2) { + done(err2); + } + }); + }); + }); + describe( 'validatePostResponse xml signature checks /', function() { var fakeClock; @@ -2283,6 +2319,7 @@ describe( 'passport-saml /', function() { } }); }); + it('returns true for valid signature', function(done) { samlObj.cacheProvider.save('_79db1e7ad12ca1d63e5b', new Date().toISOString(), function(){}); samlObj.validateRedirect(this.request, function(err, _data, success) {