Skip to content

Commit

Permalink
[ULP-2576] Add support for subject in AuthNRequests (#103)
Browse files Browse the repository at this point in the history
* [ULP-2576] Add support for subject in AuthNRequests

* add the namespace validation to the path
  • Loading branch information
Germán Lena authored Aug 31, 2020
1 parent 8427cde commit 5a7a97f
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 96 deletions.
108 changes: 56 additions & 52 deletions lib/samlp.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
var saml20 = require('saml').Saml20;
var SignedXml = require('xml-crypto').SignedXml;
var xpath = require('xpath');
var xtend = require('xtend');
var utils = require('./utils');
var templates = require('./templates');
var encoders = require('./encoders');
var saml20 = require('saml').Saml20;
var SignedXml = require('xml-crypto').SignedXml;
var xpath = require('xpath');
var xtend = require('xtend');
var utils = require('./utils');
var templates = require('./templates');
var encoders = require('./encoders');
var PassportProfileMapper = require('./claims/PassportProfileMapper');
var constants = require('./constants');
var constants = require('./constants');

function buildSamlResponse(options) {
var SAMLResponse = templates.samlresponse({
id: '_' + utils.generateUniqueID(),
instant: utils.generateInstant(),
destination: options.destination || options.audience,
inResponseTo: options.inResponseTo,
issuer: options.issuer,
id: '_' + utils.generateUniqueID(),
instant: utils.generateInstant(),
destination: options.destination || options.audience,
inResponseTo: options.inResponseTo,
issuer: options.issuer,
samlStatusCode: options.samlStatusCode,
samlStatusMessage: options.samlStatusMessage,
assertion: options.signedAssertion || ''
assertion: options.signedAssertion || ''
});

if (options.signResponse) {
options.signatureNamespacePrefix = typeof options.signatureNamespacePrefix === 'string' ? options.signatureNamespacePrefix : '' ;
options.signatureNamespacePrefix = typeof options.signatureNamespacePrefix === 'string' ? options.signatureNamespacePrefix : '';

var cannonicalized = SAMLResponse
.replace(/\r\n/g, '')
.replace(/\n/g,'')
.replace(/\n/g, '')
.replace(/>(\s*)</g, '><') //unindent
.trim();

Expand All @@ -48,7 +48,7 @@ function buildSamlResponse(options) {
}
};

sig.computeSignature(cannonicalized, { prefix: options.signatureNamespacePrefix, location: { action: 'after', reference: "//*[local-name(.)='Issuer']" }});
sig.computeSignature(cannonicalized, { prefix: options.signatureNamespacePrefix, location: { action: 'after', reference: "//*[local-name(.)='Issuer']" } });
SAMLResponse = sig.getSignedXml();
}

Expand All @@ -63,7 +63,7 @@ function nameIdentiferNotFoundErrorMessage(options) {

function getSamlResponse(options, user, callback) {
options.profileMapper = options.profileMapper || PassportProfileMapper;
options.signatureNamespacePrefix = typeof options.signatureNamespacePrefix === 'string' ? options.signatureNamespacePrefix : '' ;
options.signatureNamespacePrefix = typeof options.signatureNamespacePrefix === 'string' ? options.signatureNamespacePrefix : '';

var profileMap = options.profileMapper(user);
var claims = profileMap.getClaims(options);
Expand All @@ -76,25 +76,25 @@ function getSamlResponse(options, user, callback) {
}

saml20.create({
signatureAlgorithm: options.signatureAlgorithm,
digestAlgorithm: options.digestAlgorithm,
cert: options.cert,
key: options.key,
issuer: options.issuer,
lifetimeInSeconds: options.lifetimeInSeconds || 3600,
audiences: options.audience,
attributes: claims,
nameIdentifier: ni.nameIdentifier,
signatureAlgorithm: options.signatureAlgorithm,
digestAlgorithm: options.digestAlgorithm,
cert: options.cert,
key: options.key,
issuer: options.issuer,
lifetimeInSeconds: options.lifetimeInSeconds || 3600,
audiences: options.audience,
attributes: claims,
nameIdentifier: ni.nameIdentifier,
nameIdentifierFormat: ni.nameIdentifierFormat || options.nameIdentifierFormat,
recipient: options.recipient,
inResponseTo: options.inResponseTo,
recipient: options.recipient,
inResponseTo: options.inResponseTo,
authnContextClassRef: options.authnContextClassRef,
encryptionPublicKey: options.encryptionPublicKey,
encryptionCert: options.encryptionCert,
sessionIndex: options.sessionIndex,
typedAttributes: options.typedAttributes,
includeAttributeNameFormat: options.includeAttributeNameFormat,
signatureNamespacePrefix: options.signatureNamespacePrefix
encryptionPublicKey: options.encryptionPublicKey,
encryptionCert: options.encryptionCert,
sessionIndex: options.sessionIndex,
typedAttributes: options.typedAttributes,
includeAttributeNameFormat: options.includeAttributeNameFormat,
signatureNamespacePrefix: options.signatureNamespacePrefix
}, function (err, signedAssertion) {
if (err) return callback(err);

Expand Down Expand Up @@ -122,8 +122,8 @@ function getSamlResponse(options, user, callback) {
* @param {[type]} options [description]
* @return {[type]} [description]
*/
module.exports.auth = function(options) {
options.getUserFromRequest = options.getUserFromRequest || function(req){ return req.user; };
module.exports.auth = function (options) {
options.getUserFromRequest = options.getUserFromRequest || function (req) { return req.user; };
options.signatureAlgorithm = options.signatureAlgorithm || 'rsa-sha256';
options.digestAlgorithm = options.digestAlgorithm || 'sha256';

Expand All @@ -134,13 +134,13 @@ module.exports.auth = function(options) {
return function (req, res, next) {
var opts = xtend({}, options || {}); // clone options

if(req.method === 'GET' && req.query.Signature){
if (req.method === 'GET' && req.query.Signature) {
opts.signature = req.query.Signature;
opts.sigAlg = req.query.SigAlg;
opts.relayState = opts.RelayState || req.query.RelayState;
}

function execute (postUrl, audience, req, res, next) {
function execute(postUrl, audience, req, res, next) {
var user = opts.getUserFromRequest(req);
if (!user) return res.send(401);

Expand All @@ -157,21 +157,21 @@ module.exports.auth = function(options) {
} else {
res.set('Content-Type', 'text/html');
res.send(templates.form({
type: 'SAMLResponse',
callback: postUrl,
RelayState: opts.RelayState || (req.query || {}).RelayState || (req.body || {}).RelayState || '',
token: response.toString('base64')
type: 'SAMLResponse',
callback: postUrl,
RelayState: opts.RelayState || (req.query || {}).RelayState || (req.body || {}).RelayState || '',
token: response.toString('base64')
}));
}
});
}

utils.parseSamlRequest(req, (req.query || {}).SAMLRequest || (req.body || {}).SAMLRequest, "AUTHN_REQUEST", opts, function(err, samlRequestDom) {
utils.parseSamlRequest(req, (req.query || {}).SAMLRequest || (req.body || {}).SAMLRequest, "AUTHN_REQUEST", opts, function (err, samlRequestDom) {
if (err) return next(err);

var audience = opts.audience;
if (samlRequestDom) {
if (!audience){
if (!audience) {
var issuer = xpath.select("//*[local-name(.)='Issuer' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:assertion']/text()", samlRequestDom);
if (issuer && issuer.length > 0)
audience = issuer[0].textContent;
Expand All @@ -191,8 +191,8 @@ module.exports.auth = function(options) {
};
};

module.exports.parseRequest = function(req, options, callback) {
if (typeof options === 'function'){
module.exports.parseRequest = function (req, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
Expand All @@ -201,7 +201,7 @@ module.exports.parseRequest = function(req, options, callback) {
if (!samlRequest)
return callback();

utils.parseSamlRequest(req, samlRequest, "AUTHN_REQUEST", options, function(err, samlRequestDom) {
utils.parseSamlRequest(req, samlRequest, "AUTHN_REQUEST", options, function (err, samlRequestDom) {
if (err) {
return callback(err);
}
Expand All @@ -210,6 +210,10 @@ module.exports.parseRequest = function(req, options, callback) {
var issuer = xpath.select("//*[local-name(.)='Issuer' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:assertion']/text()", samlRequestDom);
if (issuer && issuer.length > 0) data.issuer = issuer[0].textContent;


var subject = xpath.select("//*[local-name(.)='Subject' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:assertion']/*[local-name(.)='NameID']", samlRequestDom);
if (subject && subject.length > 0) data.subject = subject[0].textContent;

var assertionConsumerUrl = samlRequestDom.documentElement.getAttribute('AssertionConsumerServiceURL');
if (assertionConsumerUrl) data.assertionConsumerServiceURL = assertionConsumerUrl;

Expand Down Expand Up @@ -248,10 +252,10 @@ module.exports.sendError = function (options) {

res.set('Content-Type', 'text/html');
res.send(templates.form({
type: 'SAMLResponse',
callback: postUrl,
RelayState: options.RelayState,
token: response.toString('base64')
type: 'SAMLResponse',
callback: postUrl,
RelayState: options.RelayState,
token: response.toString('base64')
}));
}

Expand Down
Loading

0 comments on commit 5a7a97f

Please sign in to comment.