From 321d6bd3df3637309ec4a7a34ce015b8f4f72d60 Mon Sep 17 00:00:00 2001 From: Daniel Hwang Date: Tue, 4 Aug 2015 22:21:30 -0700 Subject: [PATCH] generateServiceProviderMetadata: remove callbackUrl dependency --- README.md | 1 + lib/passport-saml/saml.js | 41 ++++++++++++++++++++++++--------------- test/tests.js | 33 +++++++++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index fd01e2fa..72c9e395 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Config parameter details: * `callbackUrl`: full callbackUrl (overrides path/protocol if supplied) * `path`: path to callback; will be combined with protocol and server host information to construct callback url if `callbackUrl` is not specified (default: `/saml/consume`) * `protocol`: protocol for callback; will be combined with path and server host information to construct callback url if `callbackUrl` is not specified (default: `http://`) + * `host`: host for callback; will be combined with path and protocol to construct callback url if `callbackUrl` is not specified (default: `localhost`) * `entryPoint`: identity provider entrypoint * `issuer`: issuer string to supply to identity provider * `cert`: see 'security and signatures' diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 889858b6..b11ea74e 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -27,6 +27,10 @@ SAML.prototype.initialize = function (options) { options.path = '/saml/consume'; } + if (!options.host) { + options.host = 'localhost'; + } + if (!options.issuer) { options.issuer = 'onelogin_saml'; } @@ -65,6 +69,25 @@ SAML.prototype.initialize = function (options) { return options; }; +SAML.prototype.getProtocol = function (req) { + return this.options.protocol || (req.protocol || 'http').concat('://'); +}; + +SAML.prototype.getCallbackUrl = function (req) { + // Post-auth destination + if (this.options.callbackUrl) { + return this.options.callbackUrl; + } else { + var host; + if (req.headers) { + host = req.headers.host; + } else { + host = this.options.host; + } + return this.getProtocol(req) + host + this.options.path; + } +}; + SAML.prototype.generateUniqueID = function () { var chars = "abcdef0123456789"; var uniqueID = ""; @@ -88,8 +111,6 @@ SAML.prototype.generateAuthorizeRequest = function (req, isPassive, callback) { var self = this; var id = "_" + self.generateUniqueID(); var instant = self.generateInstant(); - var protocol = self.options.protocol || (req.protocol || 'http').concat('://'); - var callbackUrl; var forceAuthn = self.options.forceAuthn || false; Q.fcall(function() { @@ -100,13 +121,6 @@ SAML.prototype.generateAuthorizeRequest = function (req, isPassive, callback) { } }) .then(function(){ - // Post-auth destination - if (self.options.callbackUrl) { - callbackUrl = self.options.callbackUrl; - } else { - callbackUrl = protocol + req.headers.host + self.options.path; - } - var request = { 'samlp:AuthnRequest': { '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', @@ -114,7 +128,7 @@ SAML.prototype.generateAuthorizeRequest = function (req, isPassive, callback) { '@Version': '2.0', '@IssueInstant': instant, '@ProtocolBinding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', - '@AssertionConsumerServiceURL': callbackUrl, + '@AssertionConsumerServiceURL': self.getCallbackUrl(req), '@Destination': self.options.entryPoint, 'saml:Issuer' : { '@xmlns:saml' : 'urn:oasis:names:tc:SAML:2.0:assertion', @@ -746,11 +760,6 @@ function processValidlySignedPostRequest(self, doc, callback) { } SAML.prototype.generateServiceProviderMetadata = function( decryptionCert ) { - if (!this.options.callbackUrl) { - throw new Error( - "Unable to generate service provider metadata when callbackUrl option is not set"); - } - var metadata = { 'EntityDescriptor' : { '@xmlns': 'urn:oasis:names:tc:SAML:2.0:metadata', @@ -763,7 +772,7 @@ SAML.prototype.generateServiceProviderMetadata = function( decryptionCert ) { '@index': '1', '@isDefault': 'true', '@Binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', - '@Location': this.options.callbackUrl + '@Location': this.getCallbackUrl({}) } }, } diff --git a/test/tests.js b/test/tests.js index 6ad94657..22af706c 100644 --- a/test/tests.js +++ b/test/tests.js @@ -524,7 +524,7 @@ describe( 'passport-saml /', function() { metadata.split( '\n' ).should.eql( expectedMetadata.split( '\n' ) ); } - it( 'metadata with description key should pass', function( done ) { + it( 'config with callbackUrl and decryptionPvk should pass', function( done ) { var samlConfig = { issuer: 'http://example.serviceprovider.com', callbackUrl: 'http://example.serviceprovider.com/saml/callback', @@ -537,11 +537,40 @@ describe( 'passport-saml /', function() { done(); }); - it( 'metadata without description key should pass', function( done ) { + it( 'config with callbackUrl should pass', function( done ) { var samlConfig = { issuer: 'http://example.serviceprovider.com', callbackUrl: 'http://example.serviceprovider.com/saml/callback', + identifierFormat: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + }; + var expectedMetadata = fs.readFileSync(__dirname + '/static/expected metadata without key.xml', 'utf-8'); + + testMetadata( samlConfig, expectedMetadata ); + done(); + }); + + it( 'config with protocol, path, host, and decryptionPvk should pass', function( done ) { + var samlConfig = { + issuer: 'http://example.serviceprovider.com', + protocol: 'http://', + host: 'example.serviceprovider.com', + path: '/saml/callback', identifierFormat: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient', + decryptionPvk: fs.readFileSync(__dirname + '/static/testshib encryption pvk.pem') + }; + var expectedMetadata = fs.readFileSync(__dirname + '/static/expected metadata.xml', 'utf-8'); + + testMetadata( samlConfig, expectedMetadata ); + done(); + }); + + it( 'config with protocol, path, and host should pass', function( done ) { + var samlConfig = { + issuer: 'http://example.serviceprovider.com', + protocol: 'http://', + host: 'example.serviceprovider.com', + path: '/saml/callback', + identifierFormat: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' }; var expectedMetadata = fs.readFileSync(__dirname + '/static/expected metadata without key.xml', 'utf-8');