passport-saml
Advanced tools
Comparing version 0.14.0 to 0.15.0
@@ -111,2 +111,3 @@ var zlib = require('zlib'); | ||
var signer; | ||
var samlMessageToSign = {}; | ||
switch(this.options.signatureAlgorithm) { | ||
@@ -122,3 +123,15 @@ case 'sha256': | ||
} | ||
signer.update(querystring.stringify(samlMessage)); | ||
if (samlMessage.SAMLRequest) { | ||
samlMessageToSign.SAMLRequest = samlMessage.SAMLRequest; | ||
} | ||
if (samlMessage.SAMLResponse) { | ||
samlMessageToSign.SAMLResponse = samlMessage.SAMLResponse; | ||
} | ||
if (samlMessage.RelayState) { | ||
samlMessageToSign.RelayState = samlMessage.RelayState; | ||
} | ||
if (samlMessage.SigAlg) { | ||
samlMessageToSign.SigAlg = samlMessage.SigAlg; | ||
} | ||
signer.update(querystring.stringify(samlMessageToSign)); | ||
samlMessage.Signature = signer.sign(this.options.privateCert, 'base64'); | ||
@@ -218,2 +231,10 @@ }; | ||
if (typeof(req.user.nameQualifier) !== 'undefined') { | ||
request['samlp:LogoutRequest']['saml:NameID']['@NameQualifier'] = req.user.nameQualifier; | ||
} | ||
if (typeof(req.user.spNameQualifier) !== 'undefined') { | ||
request['samlp:LogoutRequest']['saml:NameID']['@SPNameQualifier'] = req.user.spNameQualifier; | ||
} | ||
if (req.user.sessionIndex) { | ||
@@ -342,2 +363,76 @@ request['samlp:LogoutRequest']['saml2p:SessionIndex'] = { | ||
SAML.prototype.getAuthorizeForm = function (req, callback) { | ||
var self = this; | ||
// The quoteattr() function is used in a context, where the result will not be evaluated by javascript | ||
// but must be interpreted by an XML or HTML parser, and it must absolutely avoid breaking the syntax | ||
// of an element attribute. | ||
var quoteattr = function(s, preserveCR) { | ||
preserveCR = preserveCR ? ' ' : '\n'; | ||
return ('' + s) // Forces the conversion to string. | ||
.replace(/&/g, '&') // This MUST be the 1st replacement. | ||
.replace(/'/g, ''') // The 4 other predefined entities, required. | ||
.replace(/"/g, '"') | ||
.replace(/</g, '<') | ||
.replace(/>/g, '>') | ||
// Add other replacements here for HTML only | ||
// Or for XML, only if the named entities are defined in its DTD. | ||
.replace(/\r\n/g, preserveCR) // Must be before the next replacement. | ||
.replace(/[\r\n]/g, preserveCR); | ||
}; | ||
var getAuthorizeFormHelper = function(err, buffer) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
var operation = 'authorize'; | ||
var additionalParameters = self.getAdditionalParams(req, operation); | ||
var samlMessage = { | ||
SAMLRequest: buffer.toString('base64') | ||
}; | ||
Object.keys(additionalParameters).forEach(function(k) { | ||
samlMessage[k] = additionalParameters[k] || ''; | ||
}); | ||
var formInputs = Object.keys(samlMessage).map(function(k) { | ||
return '<input type="hidden" name="' + k + '" value="' + quoteattr(samlMessage[k]) + '" />'; | ||
}).join('\r\n'); | ||
callback(null, [ | ||
'<!DOCTYPE html>', | ||
'<html>', | ||
'<head>', | ||
'<meta charset="utf-8">', | ||
'<meta http-equiv="x-ua-compatible" content="ie=edge">', | ||
'</head>', | ||
'<body onload="document.forms[0].submit()">', | ||
'<noscript>', | ||
'<p><strong>Note:</strong> Since your browser does not support JavaScript, you must press the button below once to proceed.</p>', | ||
'</noscript>', | ||
'<form method="post" action="' + encodeURI(self.options.entryPoint) + '">', | ||
formInputs, | ||
'<input type="submit" value="Submit" />', | ||
'</form>', | ||
'<script>document.forms[0].style.display="none";</script>', // Hide the form if JavaScript is enabled | ||
'</body>', | ||
'</html>' | ||
].join('\r\n')); | ||
}; | ||
self.generateAuthorizeRequest(req, self.options.passive, function(err, request) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (self.options.skipRequestCompression) { | ||
getAuthorizeFormHelper(null, new Buffer(request, 'utf8')); | ||
} else { | ||
zlib.deflateRaw(request, getAuthorizeFormHelper); | ||
} | ||
}); | ||
}; | ||
SAML.prototype.getLogoutUrl = function(req, callback) { | ||
@@ -585,2 +680,3 @@ var request = this.generateLogoutRequest(req); | ||
var subject = assertion.Subject; | ||
var subjectConfirmation, confirmData; | ||
if (subject) { | ||
@@ -593,28 +689,30 @@ var nameID = subject[0].NameID; | ||
profile.nameIDFormat = nameID[0].$.Format; | ||
profile.nameQualifier = nameID[0].$.NameQualifier; | ||
profile.spNameQualifier = nameID[0].$.SPNameQualifier; | ||
} | ||
} | ||
} | ||
var subjectConfirmation = subject[0].SubjectConfirmation ? | ||
subject[0].SubjectConfirmation[0] : null; | ||
var confirmData = subjectConfirmation && subjectConfirmation.SubjectConfirmationData ? | ||
subjectConfirmation.SubjectConfirmationData[0] : null; | ||
if (subject[0].SubjectConfirmation && subject[0].SubjectConfirmation.length > 1) { | ||
msg = 'Unable to process multiple SubjectConfirmations in SAML assertion'; | ||
throw new Error(msg); | ||
} | ||
subjectConfirmation = subject[0].SubjectConfirmation ? | ||
subject[0].SubjectConfirmation[0] : null; | ||
confirmData = subjectConfirmation && subjectConfirmation.SubjectConfirmationData ? | ||
subjectConfirmation.SubjectConfirmationData[0] : null; | ||
if (subject[0].SubjectConfirmation && subject[0].SubjectConfirmation.length > 1) { | ||
msg = 'Unable to process multiple SubjectConfirmations in SAML assertion'; | ||
throw new Error(msg); | ||
} | ||
if (subjectConfirmation) { | ||
if (confirmData && confirmData.$) { | ||
var subjectNotBefore = confirmData.$.NotBefore; | ||
var subjectNotOnOrAfter = confirmData.$.NotOnOrAfter; | ||
if (subjectConfirmation) { | ||
if (confirmData && confirmData.$) { | ||
var subjectNotBefore = confirmData.$.NotBefore; | ||
var subjectNotOnOrAfter = confirmData.$.NotOnOrAfter; | ||
var subjErr = self.checkTimestampsValidityError( | ||
nowMs, subjectNotBefore, subjectNotOnOrAfter); | ||
if (subjErr) { | ||
throw subjErr; | ||
var subjErr = self.checkTimestampsValidityError( | ||
nowMs, subjectNotBefore, subjectNotOnOrAfter); | ||
if (subjErr) { | ||
throw subjErr; | ||
} | ||
} | ||
} | ||
} | ||
// Test to see that if we have a SubjectConfirmation InResponseTo that it matches | ||
@@ -621,0 +719,0 @@ // the 'InResponseTo' attribute set in the Response |
@@ -22,2 +22,3 @@ var passport = require('passport-strategy'); | ||
this._passReqToCallback = !!options.passReqToCallback; | ||
this._authnRequestBinding = options.authnRequestBinding || 'HTTP-Redirect'; | ||
} | ||
@@ -78,10 +79,27 @@ | ||
} else { | ||
var operation = { | ||
'login-request': 'getAuthorizeUrl', | ||
'logout-request': 'getLogoutUrl' | ||
var requestHandler = { | ||
'login-request': function() { | ||
if (self._authnRequestBinding === 'HTTP-POST') { | ||
this._saml.getAuthorizeForm(req, function(err, data) { | ||
if (err) { | ||
self.error(err); | ||
} else { | ||
var res = req.res; | ||
res.send(data); | ||
} | ||
}); | ||
} else { // Defaults to HTTP-Redirect | ||
this._saml.getAuthorizeUrl(req, redirectIfSuccess); | ||
} | ||
}.bind(self), | ||
'logout-request': function() { | ||
this._saml.getLogoutUrl(req, redirectIfSuccess); | ||
}.bind(self) | ||
}[options.samlFallback]; | ||
if (!operation) { | ||
if (typeof requestHandler !== 'function') { | ||
return self.fail(); | ||
} | ||
this._saml[operation](req, redirectIfSuccess); | ||
requestHandler(); | ||
} | ||
@@ -88,0 +106,0 @@ }; |
{ | ||
"name": "passport-saml", | ||
"version": "0.14.0", | ||
"version": "0.15.0", | ||
"licenses": [ | ||
@@ -5,0 +5,0 @@ { |
@@ -65,2 +65,3 @@ Passport-SAML | ||
* `skipRequestCompression`: if set to true, the SAML request from the service provider won't be compressed. | ||
* `authnRequestBinding`: if set to `HTTP-POST`, will request authentication from IDP via HTTP POST binding, otherwise defaults to HTTP Redirect | ||
* InResponseTo Validation | ||
@@ -112,3 +113,3 @@ * `validateInResponseTo`: if truthy, then InResponseTo will be validated from incoming SAML responses | ||
Passport-SAML uses the HTTP Redirect Binding for its `AuthnRequest`s, and expects to receive the messages back via the HTTP POST binding. | ||
Passport-SAML uses the HTTP Redirect Binding for its `AuthnRequest`s (unless overridden with the `authnRequestBinding` parameter), and expects to receive the messages back via the HTTP POST binding. | ||
@@ -115,0 +116,0 @@ Authentication requests sent by Passport-SAML can be signed using RSA-SHA1. To sign them you need to provide a private key in the PEM format via the `privateCert` configuration key. For example: |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
284103
2808
211