From cc5de1f2256a30ee9d303c694fe45298bc0b1df6 Mon Sep 17 00:00:00 2001 From: Julien Wollscheid Date: Tue, 31 Mar 2026 13:29:26 +0200 Subject: [PATCH] feat: add encryption algorithm options BREAKING CHANGE: adding encryption algorithm in options(if not set, defaults to http://www.w3.org/2009/xmlenc11#aes256-gcm), adding disallowEncryptionWithInsecureAlgorithm to enforce secure encryption algorithms --- README.md | 6 ++- lib/samlp.js | 5 ++- package.json | 12 +++--- test/samlp.tests.js | 91 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5ee6128..91a1b79 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,11 @@ Options | RelayState | state of the auth process | ```req.query.RelayState || req.body.RelayState``` | | sessionIndex | the index of a particular session between the principal identified by the subject and the authenticating authority | _SessionIndex is not included_ | | responseHandler | custom response handler for SAML response f(SAMLResponse, options, req, res, next) | HTML response that POSTS to postUrl | - +| encryptionPublicKey | Public key used to encrypt the SAML assertion | +| encryptionCert | Certificate used to encrypt SAML assertion | +| encryptionAlgorithm | The encryption algorithm to encrypt saml assertion | http://www.w3.org/2009/xmlenc11#aes256-gcm ([node-xml-encryption](https://github.com/auth0/node-xml-encryption/blob/master/README.md) details the available encryption algorithms and configuration options.) | +| disallowEncryptionWithInsecureAlgorithm | If true, disallows encryption with algorithms considered insecure by [node-xml-encryption](https://github.com/auth0/node-xml-encryption/blob/master/README.md) | true | +| warnOnInsecureEncryptionAlgorithm | If true, logs a warning when using an insecure encryption algorithm (using disallowEncryptionWithInsecureAlgorithm as false) | true | Add the middleware as follows: diff --git a/lib/samlp.js b/lib/samlp.js index 19a1332..12b2859 100644 --- a/lib/samlp.js +++ b/lib/samlp.js @@ -106,7 +106,10 @@ function getSamlResponse(samlConfig, user, callback) { sessionIndex: options.sessionIndex, typedAttributes: options.typedAttributes, includeAttributeNameFormat: options.includeAttributeNameFormat, - signatureNamespacePrefix: options.signatureNamespacePrefix + signatureNamespacePrefix: options.signatureNamespacePrefix, + encryptionAlgorithm: options.encryptionAlgorithm, + disallowEncryptionWithInsecureAlgorithm: options.disallowEncryptionWithInsecureAlgorithm, + warnOnInsecureEncryptionAlgorithm: options.warnOnInsecureEncryptionAlgorithm }, function (err, samlAssertion) { if (err) return callback(err); diff --git a/package.json b/package.json index b0fd92d..64260c4 100644 --- a/package.json +++ b/package.json @@ -28,31 +28,31 @@ "license": "mit", "dependencies": { "@auth0/thumbprint": "0.0.6", + "@auth0/xmldom": "0.1.21", "auth0-id-generator": "^0.2.0", "ejs": "^3.1.8", "flowstate": "^0.4.0", "querystring": "^0.2.0", - "saml": "^3.0.1", + "saml": "^4.0.0", "xml-crypto": "^2.0.0", - "@auth0/xmldom": "0.1.21", "xpath": "0.0.5", "xtend": "^1.0.3" }, "devDependencies": { + "@commitlint/cli": "^20.3.1", + "@commitlint/config-conventional": "^20.3.1", + "@semantic-release/exec": "^7.0.3", "body-parser": "^1.15.2", "chai": "^4.2.0", "cheerio": "~0.10.7", "cheerio-select": "~0.0.3", "express": "^4.17.1", "express-session": "^1.14.2", + "husky": "^9.1.7", "istanbul": "^0.4.5", "mocha": "~8.2.1", "request": "~2.88.0", "semantic-release": "^25.0.2", - "@semantic-release/exec": "^7.0.3", - "@commitlint/cli": "^20.3.1", - "@commitlint/config-conventional": "^20.3.1", - "husky": "^9.1.7", "timekeeper": "^2.2.0", "uid2": "0.0.3" } diff --git a/test/samlp.tests.js b/test/samlp.tests.js index 54f6a84..98c723d 100644 --- a/test/samlp.tests.js +++ b/test/samlp.tests.js @@ -8,6 +8,7 @@ var zlib = require('zlib'); var encoder = require('../lib/encoders'); var fs = require('fs'); var path = require('path'); +var getSamlResponse = require('../lib/samlp').getSamlResponse; describe('samlp', function () { @@ -862,4 +863,94 @@ describe('samlp', function () { }); }); }); + + describe('when using encryption options', function () { + + describe('when using insecure algorithm', function () { + var body, $, response; + + before(function (done) { + server.options = { + encryptionPublicKey: fs.readFileSync(path.join(__dirname, 'fixture/sp1.pem')), + encryptionCert: fs.readFileSync(path.join(__dirname, 'fixture/sp1.pem')), + encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc', + disallowEncryptionWithInsecureAlgorithm: true, + warnOnInsecureEncryptionAlgorithm: false + }; + request.get({ + jar: request.jar(), + uri: 'http://localhost:5050/samlp?SAMLRequest=' + urlEncodedSAMLRequest + '&RelayState=123' + }, function (err, res, b) { + if (err) return done(err); + body = b; + response = res; + $ = cheerio.load(body); + done(); + }); + }); + + it('should return an error with disallowEncryptionWithInsecureAlgorithm set to true', function () { + expect(response.statusCode).to.equal(400); + expect(body).to.equal('encryption algorithm http://www.w3.org/2001/04/xmlenc#aes256-cbc is not secure'); + }); + }); + + describe('when using insecure encryption algorithm with disallowEncryptionWithInsecureAlgorithm set to false', function () { + var body, $, response; + + before(function (done) { + server.options = { + encryptionPublicKey: fs.readFileSync(path.join(__dirname, 'fixture/sp1.pem')), + encryptionCert: fs.readFileSync(path.join(__dirname, 'fixture/sp1.pem')), + encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc', + disallowEncryptionWithInsecureAlgorithm: false, + warnOnInsecureEncryptionAlgorithm: false + }; + request.get({ + jar: request.jar(), + uri: 'http://localhost:5050/samlp?SAMLRequest=' + urlEncodedSAMLRequest + '&RelayState=123' + }, function (err, res, b) { + if (err) return done(err); + body = b; + response = res; + $ = cheerio.load(body); + done(); + }); + }); + + it('should return success with disallowEncryptionWithInsecureAlgorithm set to false', function (done) { + expect(response.statusCode).to.equal(200); + done(); + }); + }); + + describe('when using secure encryption algorithm', function () { + var body, $, response; + + before(function (done) { + server.options = { + encryptionPublicKey: fs.readFileSync(path.join(__dirname, 'fixture/sp1.pem')), + encryptionCert: fs.readFileSync(path.join(__dirname, 'fixture/sp1.pem')), + encryptionAlgorithm: 'http://www.w3.org/2009/xmlenc11#aes256-gcm', + disallowEncryptionWithInsecureAlgorithm: true, + warnOnInsecureEncryptionAlgorithm: false + }; + request.get({ + jar: request.jar(), + uri: 'http://localhost:5050/samlp?SAMLRequest=' + urlEncodedSAMLRequest + '&RelayState=123' + }, function (err, res, b) { + if (err) return done(err); + body = b; + response = res; + $ = cheerio.load(body); + done(); + }); + }); + + it('should return success when using a secure algorithm', function (done) { + expect(response.statusCode).to.equal(200); + done(); + }); + }); + }); });