diff --git a/lib/saml11.js b/lib/saml11.js
index f359aa54..fa60a6aa 100644
--- a/lib/saml11.js
+++ b/lib/saml11.js
@@ -42,7 +42,7 @@ exports.create = function(options, callback) {
algorithms.digest[options.digestAlgorithm]);
sig.signingKey = options.key;
-
+
sig.keyInfoProvider = {
getKeyInfo: function () {
return "" + cert + "";
@@ -68,7 +68,7 @@ exports.create = function(options, callback) {
conditions[0].setAttribute('NotBefore', now.format('YYYY-MM-DDTHH:mm:ss.SSS[Z]'));
conditions[0].setAttribute('NotOnOrAfter', now.add(options.lifetimeInSeconds, 'seconds').format('YYYY-MM-DDTHH:mm:ss.SSS[Z]'));
}
-
+
if (options.audiences) {
var audiences = options.audiences instanceof Array ? options.audiences : [options.audiences];
audiences.forEach(function (audience) {
@@ -83,7 +83,7 @@ exports.create = function(options, callback) {
var statement = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'AttributeStatement')[0];
Object.keys(options.attributes).forEach(function(prop) {
if(typeof options.attributes[prop] === 'undefined') return;
-
+
//
// Foo Bar
//
@@ -110,15 +110,15 @@ exports.create = function(options, callback) {
.setAttribute('AuthenticationInstant', now.format('YYYY-MM-DDTHH:mm:ss.SSS[Z]'));
var nameID = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'NameIdentifier')[0];
-
+
if (options.nameIdentifier) {
nameID.textContent = options.nameIdentifier;
-
+
doc.getElementsByTagName('saml:AuthenticationStatement')[0]
.getElementsByTagName('saml:NameIdentifier')[0]
.textContent = options.nameIdentifier;
}
-
+
if (options.nameIdentifierFormat) {
var nameIDs = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'NameIdentifier');
nameIDs[0].setAttribute('Format', options.nameIdentifierFormat);
@@ -127,18 +127,18 @@ exports.create = function(options, callback) {
if (!options.encryptionCert) return sign(options, sig, doc, callback);
- // encryption is turned on,
+ // encryption is turned on,
var proofSecret;
async.waterfall([
function(cb) {
- if (!options.subjectConfirmationMethod && options.subjectConfirmationMethod !== 'holder-of-key')
+ if (!options.subjectConfirmationMethod && (options.subjectConfirmationMethod !== 'holder-of-key' || options.subjectConfirmationMethod !== 'sender-vouches'))
return cb();
-
+
crypto.randomBytes(32, function(err, randomBytes) {
proofSecret = randomBytes;
addSubjectConfirmation(options, doc, options.holderOfKeyProofSecret || randomBytes, cb);
});
-
+
},
function(cb) {
sign(options, sig, doc, function(err, signed) {
@@ -150,7 +150,7 @@ exports.create = function(options, callback) {
if (err) return callback(err);
callback(null, result, proofSecret);
});
-};
+};
function addSubjectConfirmation(options, doc, randomBytes, callback) {
var encryptOptions = {
@@ -159,7 +159,7 @@ function addSubjectConfirmation(options, doc, randomBytes, callback) {
keyEncryptionAlgorighm: options.keyEncryptionAlgorighm || 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
};
- xmlenc.encryptKeyInfo(randomBytes, encryptOptions, function(err, keyinfo) {
+ xmlenc.encryptKeyInfo(randomBytes, encryptOptions, function(err, keyinfo) {
if (err) return cb(err);
var subjectConfirmationNodes = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'SubjectConfirmation');
@@ -172,7 +172,12 @@ function addSubjectConfirmation(options, doc, randomBytes, callback) {
}
var method = subjectConfirmationNodes[i].getElementsByTagNameNS(NAMESPACE, 'ConfirmationMethod')[0];
- method.textContent = 'urn:oasis:names:tc:SAML:1.0:cm:holder-of-key';
+ if (options.subjectConfirmationMethod == 'holder-of-key') {
+ method.textContent = 'urn:oasis:names:tc:SAML:1.0:cm:holder-of-key';
+
+ } else if (options.subjectConfirmationMethod == 'sender-vouches') {
+ method.textContent = 'urn:oasis:names:tc:SAML:1.0:cm:sender-vouches';
+ }
subjectConfirmationNodes[i].appendChild(keyinfoDom.documentElement);
}
@@ -185,9 +190,9 @@ function sign(options, sig, doc, callback) {
var signed;
try {
- var opts = options.xpathToNodeBeforeSignature ? {
- location: {
- reference: options.xpathToNodeBeforeSignature,
+ var opts = options.xpathToNodeBeforeSignature ? {
+ location: {
+ reference: options.xpathToNodeBeforeSignature,
action: 'after'
}
} : {};
diff --git a/test/saml11.tests.js b/test/saml11.tests.js
index d924c278..fd197167 100644
--- a/test/saml11.tests.js
+++ b/test/saml11.tests.js
@@ -125,7 +125,7 @@ describe('saml 1.1', function () {
var isValid = utils.isValidSignature(signedAssertion, options.cert);
assert.equal(true, isValid);
-
+
var attributes = utils.getAttributes(signedAssertion);
assert.equal(3, attributes.length);
assert.equal('emailaddress', attributes[0].getAttribute('AttributeName'));
@@ -253,7 +253,7 @@ describe('saml 1.1', function () {
};
var signedAssertion = saml11.create(options);
var doc = new xmldom.DOMParser().parseFromString(signedAssertion);
-
+
var signature = doc.documentElement.getElementsByTagName('Signature');
assert.equal('saml:Conditions', signature[0].previousSibling.nodeName);
@@ -317,7 +317,7 @@ describe('saml 1.1', function () {
saml11.create(options, function(err, encrypted) {
if (err) return done(err);
-
+
xmlenc.decrypt(encrypted, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
if (err) return done(err);
var isValid = utils.isValidSignature(decrypted, options.cert);
@@ -327,7 +327,7 @@ describe('saml 1.1', function () {
});
});
- it('should support holder-of-key suject confirmationmethod', function (done) {
+ it('should support holder-of-key subject confirmationmethod', function (done) {
var options = {
cert: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key'),
@@ -338,10 +338,10 @@ describe('saml 1.1', function () {
saml11.create(options, function(err, encrypted, proofSecret) {
if (err) return done(err);
-
+
xmlenc.decrypt(encrypted, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
if (err) return done(err);
-
+
var doc = new xmldom.DOMParser().parseFromString(decrypted);
var subjectConfirmationNodes = doc.documentElement.getElementsByTagName('saml:SubjectConfirmation');
assert.equal(2, subjectConfirmationNodes.length);
@@ -358,6 +358,37 @@ describe('saml 1.1', function () {
});
});
+ it('should support sender-vouches subject confirmationmethod', function (done) {
+ var options = {
+ cert: fs.readFileSync(__dirname + '/test-auth0.pem'),
+ key: fs.readFileSync(__dirname + '/test-auth0.key'),
+ encryptionPublicKey: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
+ encryptionCert: fs.readFileSync(__dirname + '/test-auth0.pem'),
+ subjectConfirmationMethod: 'sender-vouches'
+ };
+
+ saml11.create(options, function(err, encrypted, proofSecret) {
+ if (err) return done(err);
+
+ xmlenc.decrypt(encrypted, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
+ if (err) return done(err);
+
+ var doc = new xmldom.DOMParser().parseFromString(decrypted);
+ var subjectConfirmationNodes = doc.documentElement.getElementsByTagName('saml:SubjectConfirmation');
+ assert.equal(2, subjectConfirmationNodes.length);
+ for (var i=0;i