From 0b94d9f45830a2f7a1d694555d98ad49f26b7f1a Mon Sep 17 00:00:00 2001 From: plessbd Date: Wed, 24 Sep 2014 17:04:00 -0400 Subject: [PATCH 1/4] Update saml20.js Allow for modified timestamps --- lib/saml20.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/saml20.js b/lib/saml20.js index 9ff4de28..c64e0bf8 100644 --- a/lib/saml20.js +++ b/lib/saml20.js @@ -1,4 +1,3 @@ - var utils = require('./utils'), Parser = require('xmldom').DOMParser, SignedXml = require('xml-crypto').SignedXml, @@ -31,6 +30,7 @@ exports.create = function(options, callback) { options.signatureAlgorithm = options.signatureAlgorithm || 'rsa-sha256'; options.digestAlgorithm = options.digestAlgorithm || 'sha256'; + options.timeStampFormat = options.timeStampFormat || 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'; var cert = utils.pemToCert(options.cert); @@ -55,15 +55,15 @@ exports.create = function(options, callback) { } var now = moment.utc(); - doc.documentElement.setAttribute('IssueInstant', now.format('YYYY-MM-DDTHH:mm:ss.SSS[Z]')); + doc.documentElement.setAttribute('IssueInstant', now.format(options.timeStampFormat)); var conditions = doc.documentElement.getElementsByTagName('saml:Conditions'); var confirmationData = doc.documentElement.getElementsByTagName('saml:SubjectConfirmationData'); if (options.lifetimeInSeconds) { - conditions[0].setAttribute('NotBefore', now.format('YYYY-MM-DDTHH:mm:ss.SSS[Z]')); - conditions[0].setAttribute('NotOnOrAfter', now.clone().add('seconds', options.lifetimeInSeconds).format('YYYY-MM-DDTHH:mm:ss.SSS[Z]')); + conditions[0].setAttribute('NotBefore', now.format(options.timeStampFormat)); + conditions[0].setAttribute('NotOnOrAfter', now.clone().add('seconds', options.lifetimeInSeconds).format(options.timeStampFormat)); - confirmationData[0].setAttribute('NotOnOrAfter', now.clone().add('seconds', options.lifetimeInSeconds).format('YYYY-MM-DDTHH:mm:ss.SSS[Z]')); + confirmationData[0].setAttribute('NotOnOrAfter', now.clone().add('seconds', options.lifetimeInSeconds).format(options.timeStampFormat)); } if (options.audiences) { @@ -104,7 +104,7 @@ exports.create = function(options, callback) { } doc.getElementsByTagName('saml:AuthnStatement')[0] - .setAttribute('AuthnInstant', now.format('YYYY-MM-DDTHH:mm:ss.SSS[Z]')); + .setAttribute('AuthnInstant', now.format(options.timeStampFormat)); var nameID = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'NameID')[0]; @@ -145,4 +145,3 @@ exports.create = function(options, callback) { callback(null, utils.removeWhitespace(encrypted)); }); }; - From f62ea830cc37264b6f135e266feb3f4e91c3ccf8 Mon Sep 17 00:00:00 2001 From: Stephan DeSouza Date: Mon, 7 Mar 2016 20:38:21 -0500 Subject: [PATCH 2/4] Added tests for a SessionNotOnOrAfter on saml:AuthnStatement. --- test/saml20.tests.js | 41 +++++++++++++++++++++++++++++++++++++++++ test/utils.js | 5 +++++ 2 files changed, 46 insertions(+) diff --git a/test/saml20.tests.js b/test/saml20.tests.js index 39371290..9f21c2ac 100644 --- a/test/saml20.tests.js +++ b/test/saml20.tests.js @@ -83,6 +83,47 @@ describe('saml 2.0', function () { assert.equal('http://example.org/claims/testaccent', attributes[2].getAttribute('Name')); assert.equal('fóo', attributes[2].textContent); }); + + it('should set SessionNotOnOrAfter on authnStatement when specified', function () { + var options = { + cert: fs.readFileSync(__dirname + '/test-auth0.pem'), + key: fs.readFileSync(__dirname + '/test-auth0.key'), + sessionLifetimeInSeconds: 3600 + }; + + var signedAssertion = saml.create(options); + + var authnStatement = utils.getAuthnStatement(signedAssertion); + assert.equal(1, authnStatement.length); + var authnInstant = authnStatement[0].getAttribute('AuthnInstant'); + var sessionNotOnOrAfter = authnStatement[0].getAttribute('SessionNotOnOrAfter'); + should.ok(authnInstant); + should.ok(sessionNotOnOrAfter); + + var lifetime = Math.round((moment(sessionNotOnOrAfter).utc() - moment(authnInstant).utc()) / 1000); + assert.equal(3600, lifetime); + var isValid = utils.isValidSignature(signedAssertion, options.cert); + assert.equal(true, isValid); + }); + + it('should not set SessionNotOnOrAfter on authnStatement when not specified', function () { + var options = { + cert: fs.readFileSync(__dirname + '/test-auth0.pem'), + key: fs.readFileSync(__dirname + '/test-auth0.key'), + }; + + var signedAssertion = saml.create(options); + + var authnStatement = utils.getAuthnStatement(signedAssertion); + assert.equal(1, authnStatement.length); + var authnInstant = authnStatement[0].getAttribute('AuthnInstant'); + var sessionNotOnOrAfter = authnStatement[0].hasAttribute('SessionNotOnOrAfter'); + should.ok(authnInstant); + assert.equal(false, sessionNotOnOrAfter); + + var isValid = utils.isValidSignature(signedAssertion, options.cert); + assert.equal(true, isValid); + }); it('whole thing with specific authnContextClassRef', function () { var options = { diff --git a/test/utils.js b/test/utils.js index f01b9cc1..b276fe9c 100644 --- a/test/utils.js +++ b/test/utils.js @@ -52,6 +52,11 @@ exports.getAuthenticationStatement = function(assertion) { .getElementsByTagName('saml:AuthenticationStatement')[0]; }; +exports.getAuthnStatement = function(assertion) { + var doc = new xmldom.DOMParser().parseFromString(assertion); + return doc.documentElement.getElementsByTagName('saml:AuthnStatement'); +}; + exports.getAttributes = function(assertion) { var doc = new xmldom.DOMParser().parseFromString(assertion); return doc.documentElement From b2b26a044ed375cf421952c228d43cfad0d68cb0 Mon Sep 17 00:00:00 2001 From: Stephan DeSouza Date: Mon, 7 Mar 2016 20:53:18 -0500 Subject: [PATCH 3/4] Added option for a SessionNotOnOrAfter on saml:AuthnStatement. --- lib/saml20.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/saml20.js b/lib/saml20.js index 7e53cd02..231280ea 100644 --- a/lib/saml20.js +++ b/lib/saml20.js @@ -106,8 +106,12 @@ exports.create = function(options, callback) { }); } - doc.getElementsByTagName('saml:AuthnStatement')[0] - .setAttribute('AuthnInstant', now.format(options.timeStampFormat)); + var authnStatement = doc.getElementsByTagName('saml:AuthnStatement')[0]; + authnStatement.setAttribute('AuthnInstant', now.format(options.timeStampFormat)); + if (options.sessionLifetimeInSeconds) { + authnStatement.setAttribute('SessionNotOnOrAfter', now.clone().add('seconds', options.sessionLifetimeInSeconds).format(options.timeStampFormat)); + } + var nameID = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'NameID')[0]; From ad17ed1c4a023b8fe293cdedccc4e820c351375c Mon Sep 17 00:00:00 2001 From: Stephan DeSouza Date: Mon, 7 Mar 2016 20:53:37 -0500 Subject: [PATCH 4/4] Version 0.8.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 71496a2b..84817f23 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "saml", - "version": "0.8.0", + "version": "0.8.1", "devDependencies": { "mocha": "*", "should": "~1.2.1"