Skip to content

Commit

Permalink
add support for custom assertion request
Browse files Browse the repository at this point in the history
  • Loading branch information
jfromaniello committed Jun 6, 2014
1 parent 3a84079 commit fadefc3
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 56 deletions.
55 changes: 37 additions & 18 deletions lib/passport-wsfed-saml2/samlp.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,51 @@ var Samlp = module.exports = function Samlp (options, saml) {
this._saml = saml;
};

var supplant = function (tmpl, o) {
return tmpl.replace(/\@\@([^\@]*)\@\@/g,
function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
}
);
};

var trimXml = function (xml) {
return xml.replace(/\r\n/g, '')
.replace(/\n/g,'')
.replace(/>(\s*)</g, '><') //unindent
.trim();
};

Samlp.prototype = {
getSamlRequestUrl: function (opts, callback) {
var options = xtend(opts || {}, this.options);

var SAMLRequest = templates.samlrequest({
var assert_and_destination = templates.assert_and_destination({
Destination: options.identityProviderUrl,
AssertionConsumerServiceURL: options.callback
});

var model = {
ID: '_' + generateUniqueID(),
IssueInstant: generateInstant(),
Destination: options.identityProviderUrl,
Issuer: options.realm,
ProtocolBinding: options.protocolBinding,
AssertionConsumerServiceURL: options.callback
}).replace(/\r\n/g, '')
.replace(/\n/g,'')
.replace(/>(\s*)</g, '><') //unindent
.trim();
AssertServiceURLAndDestination: assert_and_destination
};

var SAMLRequest = trimXml(!options.requestTemplate ? templates.samlrequest(model) : supplant(options.requestTemplate, model));

if (options.deflate) {
zlib.deflateRaw(new Buffer(SAMLRequest), function(err, buffer) {
if (err) return callback(err);

callback(null, buildUrl(buffer));
callback(null, buildUrl(buffer));
});
} else {
callback(null, buildUrl(new Buffer(SAMLRequest)));
callback(null, buildUrl(new Buffer(SAMLRequest)));
}

function buildUrl(buffer) {
var parsed = url.parse(options.identityProviderUrl, true);
var samlRequest = options.identityProviderUrl.split('?')[0] + '?' + qs.encode( xtend(parsed.query, { SAMLRequest: buffer.toString('base64'), RelayState: options.RelayState || '' }));
Expand Down Expand Up @@ -98,7 +117,7 @@ Samlp.prototype = {

validateSamlResponse: function (samlResponse, callback) {
var self = this;

if (typeof samlResponse === 'string') {
samlResponse = new xmldom.DOMParser().parseFromString(samlResponse);
}
Expand All @@ -116,24 +135,24 @@ Samlp.prototype = {

if (!isResponseSigned && !isAssertionSigned) {
return callback(new Error('neither the response nor the assertion are signed'));
}
}

if (isResponseSigned) {
self._saml.validateSignature(samlResponse, {
cert: self.options.cert,
thumbprint: self.options.thumbprint,
self._saml.validateSignature(samlResponse, {
cert: self.options.cert,
thumbprint: self.options.thumbprint,
signaturePath: samlResponseSignaturePath
},
},
function (err) {
if (err) { return callback(err); }

if (!isAssertionSigned) {
return self._saml.parseAssertion(assertion, callback);
}

return self._saml.validateSamlAssertion(assertion, callback);
});
}
}
else if (isAssertionSigned) {
return self._saml.validateSamlAssertion(assertion, callback);
}
Expand Down
4 changes: 4 additions & 0 deletions lib/passport-wsfed-saml2/templates/assert_and_destination.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<% if (AssertionConsumerServiceURL) { -%>
AssertionConsumerServiceURL="<%= AssertionConsumerServiceURL %>"
Destination="<%= Destination %>"
<% } -%>
13 changes: 6 additions & 7 deletions lib/passport-wsfed-saml2/templates/samlrequest.ejs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
<% if (AssertionConsumerServiceURL) { %> AssertionConsumerServiceURL="<%= AssertionConsumerServiceURL %>" <% } %>
<% if (AssertionConsumerServiceURL) { %> Destination="<%= Destination %>" <% } %>
ID="<%= ID %>"
IssueInstant="<%= IssueInstant %>"
ProtocolBinding="<%= ProtocolBinding %>" Version="2.0">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"><%= Issuer %></saml:Issuer>
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
<%= AssertServiceURLAndDestination %>
ID="<%= ID %>"
IssueInstant="<%= IssueInstant %>"
ProtocolBinding="<%= ProtocolBinding %>" Version="2.0">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"><%= Issuer %></saml:Issuer>
</samlp:AuthnRequest>
40 changes: 26 additions & 14 deletions test/fixture/samlp-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,23 @@ var Strategy = require('../../lib/passport-wsfed-saml2').Strategy;
var identityProviderUrl = 'http://localhost:5051/samlp';
var relayState = 'somestate';

passport.use('samlp', new Strategy(
{
passport.use('samlp', new Strategy({
path: '/callback',
realm: 'https://auth0-dev-ed.my.salesforce.com',
identityProviderUrl: identityProviderUrl,
thumbprint: '5ca6e1202eafc0a63a5b93a43572eb2376fed309'
},
function(profile, done) {
}, function(profile, done) {
return done(null, profile);
})
);

passport.use('samlp-custom-request-template', new Strategy({
path: '/callback',
realm: 'https://auth0-dev-ed.my.salesforce.com',
identityProviderUrl: identityProviderUrl,
thumbprint: '5ca6e1202eafc0a63a5b93a43572eb2376fed309',
requestTemplate: '<AuthnRequest Issuertico="@@Issuer@@" Version="3.0" Protocol="@@ProtocolBinding@@"></AuthnRequest>'
}, function(profile, done) {
return done(null, profile);
})
);
Expand Down Expand Up @@ -113,7 +122,7 @@ passport.use('samlp-with-utf8', new Strategy(
thumbprint: '42FA24A83E107F6842E05D2A2CA0A0A0CA8A2031',
decryptionKey: fs.readFileSync(path.join(__dirname, '../test-decryption.key')),
checkExpiration: false, // we are using a precomputed assertion generated from a sample idp feide
checkAudience: false
checkAudience: false
},
function(profile, done) {
return done(null, profile);
Expand Down Expand Up @@ -167,7 +176,7 @@ module.exports.start = function(options, callback){
}

//configure samlp middleware
app.get('/samlp', function(req, res, next) {
app.get('/samlp', function(req, res, next) {
samlp.auth(xtend({}, {
issuer: 'urn:fixture-test',
getPostURL: getPostURL,
Expand All @@ -179,7 +188,10 @@ module.exports.start = function(options, callback){
app.get('/login', passport.authenticate('samlp', { protocol: 'samlp', RelayState: relayState }));
app.get('/login-idp-with-querystring', passport.authenticate('samlp-idpurl-with-querystring', { protocol: 'samlp', RelayState: relayState }));

app.post('/callback',
app.get('/login-custom-request-template',
passport.authenticate('samlp-custom-request-template', { protocol: 'samlp', RelayState: relayState }));

app.post('/callback',
function(req, res, next) {
//console.log('req.body');
next();
Expand All @@ -190,49 +202,49 @@ module.exports.start = function(options, callback){
}
);

app.post('/callback/samlp-signedresponse',
app.post('/callback/samlp-signedresponse',
passport.authenticate('samlp-signedresponse', { protocol: 'samlp' }),
function(req, res) {
res.json(req.user);
}
);

app.post('/callback/samlp-signedresponse-invalidcert',
app.post('/callback/samlp-signedresponse-invalidcert',
passport.authenticate('samlp-signedresponse-invalidcert', { protocol: 'samlp' }),
function(req, res) {
res.json(req.user);
}
);

app.post('/callback/samlp-invalidcert',
app.post('/callback/samlp-invalidcert',
passport.authenticate('samlp-invalidcert', { protocol: 'samlp' }),
function(req, res) {
res.json(req.user);
}
);

app.post('/callback/samlp-signedresponse-signedassertion',
app.post('/callback/samlp-signedresponse-signedassertion',
passport.authenticate('samlp-signedresponse-signedassertion', { protocol: 'samlp' }),
function(req, res) {
res.json(req.user);
}
);

app.post('/callback/samlp-ping',
app.post('/callback/samlp-ping',
passport.authenticate('samlp-ping', { protocol: 'samlp' }),
function(req, res) {
res.json(req.user);
}
);

app.post('/callback/samlp-okta',
app.post('/callback/samlp-okta',
passport.authenticate('samlp-okta', { protocol: 'samlp' }),
function(req, res) {
res.json(req.user);
}
);

app.post('/callback/samlp-with-utf8',
app.post('/callback/samlp-with-utf8',
passport.authenticate('samlp-with-utf8', { protocol: 'samlp' }),
function(req, res) {
res.json(req.user);
Expand Down
Loading

0 comments on commit fadefc3

Please sign in to comment.