From 20d6fe8be6a85e4c088ad9a2557cf96bbf055b4b Mon Sep 17 00:00:00 2001 From: hauntingEcho Date: Thu, 16 Nov 2017 12:05:17 -0600 Subject: [PATCH] added SessionNotOnOrAfter support to Saml2.0, added jsdoc-style documentation to saml20.create --- lib/saml20.js | 37 +++++++++++++++++++++++++++++++++++++ test/saml20.tests.js | 19 ++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/lib/saml20.js b/lib/saml20.js index 21c1f278..9c773ed7 100644 --- a/lib/saml20.js +++ b/lib/saml20.js @@ -55,6 +55,38 @@ function getNameFormat(name){ return 'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified'; } +/** + * + * @param {object} options + * @param options.key + * @param options.cert + * @param {string} [options.signatureAlgorithm] + * @param {string} [options.digestAlgorithm] + * @param {boolean} [options.includeAttributeNameFormat=true] + * @param [options.typedAttributes] + * @param {string} [options.issuer] Issuer of the SAML assertion + * @param [options.lifetimeInSeconds] timeframe in which this assertion is valid + * @param [options.recipient] recipient for the confirmation data + * @param [options.audiences] + * @param {string} [options.inResponseTo] SAML SubjectConfirmationData@InResponseTo + * @param {object} [options.attributes] assertion attributes. {@link https://github.com/auth0/node-saml/issues/33 Do not pass an empty object} + * @param [options.sessionIndex] session this assertion applies to + * @param {int} [options.sessionNotOnOrAfterSeconds] after this timestamp, the service provider must reauthenticate + * @param {string} [options.nameIdentifier] SAML nameID + * @param [options.nameIdentifierFormat] SAML nameID format + * @param [options.authnContextClassRef] + * @param [options.xpathToNodeBeforeSignature] + * @param [options.encryptionCert] + * @param [options.encryptionPublicKey] + * @param [options.encryptionAlgorithm] + * @param [options.keyEncryptionAlgorighm] {@link https://github.com/auth0/node-xml-encryption/issues/19 note spelling} + * @param {string} [options.prefix] + * @param {string|number} [options.signatureNamespacePrefix] + * @param {string} [options.uid] SAML document ID + * @param {function} [callback] required if options.encryptionCert is specified + * @return {string|T|null} + * @template T + */ exports.create = function(options, callback) { if (!options.key) throw new Error('Expect a private key in pem format'); @@ -175,6 +207,11 @@ exports.create = function(options, callback) { .setAttribute('SessionIndex', options.sessionIndex); } + if (options.sessionNotOnOrAfterSeconds) { + doc.getElementsByTagName('saml:AuthnStatement')[0] + .setAttribute('SessionNotOnOrAfter', moment.unix(options.sessionNotOnOrAfterSeconds).utc().format('YYYY-MM-DDTHH:mm:ss.SSS[Z]')); + } + var nameID = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'NameID')[0]; if (options.nameIdentifier) { diff --git a/test/saml20.tests.js b/test/saml20.tests.js index e351cfa3..a5432dda 100644 --- a/test/saml20.tests.js +++ b/test/saml20.tests.js @@ -168,7 +168,7 @@ describe('saml 2.0', function () { assert.equal('123', attributes[4].textContent); }); - it('should set attributes to anytpe when typedAttributes is false', function () { + it('should set attributes to anyType when typedAttributes is false', function () { var options = { cert: fs.readFileSync(__dirname + '/test-auth0.pem'), key: fs.readFileSync(__dirname + '/test-auth0.key'), @@ -439,7 +439,24 @@ describe('saml 2.0', function () { var signature = doc.documentElement.getElementsByTagName('Signature'); assert.equal('saml:Conditions', signature[0].previousSibling.nodeName); }); + + it('should add SessionNotOnOrAfter when specified', function() { + var now = 5000; + var options = { + cert: fs.readFileSync(__dirname + '/test-auth0.pem'), + key: fs.readFileSync(__dirname + '/test-auth0.key'), + sessionNotOnOrAfterSeconds: now + }; + + var signedAssertion = saml.create(options); + var isValid = utils.isValidSignature(signedAssertion, options.cert); + assert.equal(true, isValid); + + var doc = new xmldom.DOMParser().parseFromString(signedAssertion, 'text/xml'); + var sessionNotOnOrAfter = doc.documentElement.getElementsByTagName('saml:AuthnStatement')[0].getAttribute('SessionNotOnOrAfter'); + assert.equal(sessionNotOnOrAfter, '1970-01-01T01:23:20.000Z'); + }); it('should not include AudienceRestriction when there are no audiences', function () { var options = {