diff --git a/Dockerfile b/Dockerfile
index 153014168..e6446a3e5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -11,11 +11,15 @@ EXPOSE 7000 7000
# ADD ./node_modules node_modules
ADD ./lib lib
+ADD ./templates templates
ADD ./views views
ADD ./app.js app.js
ADD ./config.js config.js
+add ./idp-metadata.js idp-metadata.js
ADD ./idp-public-cert.pem idp-public-cert.pem
ADD ./idp-private-key.pem idp-private-key.pem
+ADD ./sp-cert.pem sp-cert.pem
+ADD ./sp-key.pem sp-key.pem
ADD ./public public
-ENTRYPOINT [ "node", "app.js", "--acs", "https://deptva-vetsgov-eval.okta.com/sso/saml2/0oa1pbnlkmlWpo0q22p7", "--issuer", "samlproxy-idp.vetsgov.dev", "--aud", "https://www.okta.com/saml2/service-provider/spshbtcxwhreqinrtome", "--ibu", "https://dev.vets.gov/samlproxy/idp" ]
+ENTRYPOINT ["node", "app.js", "--idpAcsUrl", "https://deptva-vetsgov-eval.okta.com/sso/saml2/0oa1pbnlkmlWpo0q22p7", "--idpIssuer", "samlproxy-idp.vetsgov.dev", "--idpAudience", "https://www.okta.com/saml2/service-provider/spshbtcxwhreqinrtome", "--idpBaseUrl", "https://dev.vets.gov/samlproxy/idp", "--spIdpMetaUrl", "https://api.idmelabs.com/saml/metadata/provider", "--spNameIDFormat", "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "--spAudience", "samlproxy.vetsgov.dev", "--spIdpIssuer", "api.idmelabs.com", "--spAuthnContextClassRef", "http://idmanagement.gov/ns/assurance/loa/3", "--spAcsUrls", "/samlproxy/sp/saml/sso"]
diff --git a/app.js b/app.js
index d13524c1e..f02c28cde 100644
--- a/app.js
+++ b/app.js
@@ -4,6 +4,7 @@
*/
const express = require('express'),
+ _ = require('underscore'),
os = require('os'),
fs = require('fs'),
http = require('http'),
@@ -18,9 +19,14 @@ const express = require('express'),
yargs = require('yargs/yargs'),
xmlFormat = require('xml-formatter'),
samlp = require('samlp'),
+ SamlStrategy = require('passport-wsfed-saml2').Strategy,
+ passport = require('passport'),
+ PassportSaml = require('passport-wsfed-saml2').SAML,
+ PassportSamlp = require('passport-wsfed-saml2').samlp,
Parser = require('xmldom').DOMParser,
SessionParticipants = require('samlp/lib/sessionParticipants'),
- SimpleProfileMapper = require('./lib/simpleProfileMapper.js');
+ SimpleProfileMapper = require('./lib/simpleProfileMapper.js'),
+ IdPMetadata = require('./idp-metadata');
/**
* Globals
@@ -35,12 +41,42 @@ const IDP_PATHS = {
SETTINGS: '/samlproxy/idp/settings'
}
+const AUTHN_REQUEST_TEMPLATE = _.template(
+ fs.readFileSync(path.join(__dirname, '/templates/authnrequest.tpl'), 'utf8')
+);
+const METADATA_TEMPLATE = _.template(
+ fs.readFileSync(path.join(__dirname, '/templates/metadata.tpl'), 'utf8')
+);
+
+const SP_SLO_URL = '/samlproxy/sp/saml/slo';
+const SP_PROFILE_URL = '/samlproxy/sp/profile';
+const SP_LOGIN_URL ='/samlproxy/sp/login';
+const SP_LOGOUT_URL = '/samlproxy/sp/logout';
+const SP_METADATA_URL = '/samlproxy/sp/metadata';
+const SP_SETTINGS_URL = '/samlproxy/sp/settings';
+const SP_ERROR_URL = '/samlproxy/sp/error';
+
+const BINDINGS = {
+ REDIRECT: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
+ POST: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
+}
+
+const NAMEID_FORMAT_PREFERENCE = [
+ 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
+ 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
+ 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
+ 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
+ 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos',
+ 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName'
+]
+
+
const cryptTypes = {
- certificate: /-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----/,
- 'RSA private key': /-----BEGIN RSA PRIVATE KEY-----\n[^-]*\n-----END RSA PRIVATE KEY-----/,
- 'public key': /-----BEGIN PUBLIC KEY-----\n[^-]*\n-----END PUBLIC KEY-----/,
- },
- KEY_CERT_HELP_TEXT = "Please generate a key-pair for the IdP using the following openssl command:\n" +
+ certificate: /-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----/,
+ 'RSA private key': /-----BEGIN RSA PRIVATE KEY-----\n[^-]*\n-----END RSA PRIVATE KEY-----/,
+ 'public key': /-----BEGIN PUBLIC KEY-----\n[^-]*\n-----END PUBLIC KEY-----/,
+},
+ KEY_CERT_HELP_TEXT = "Please generate a key-pair for the IdP using the following openssl command:\n" +
"\topenssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' -keyout idp-private-key.pem -out idp-public-cert.pem -days 7300";
@@ -94,11 +130,22 @@ function makeCertFileCoercer(type, description, helpText) {
}
throw new Error(
'Invalid ' + description + ', not a valid crypt cert/key or file path' +
- (helpText ? '\n' + helpText : '')
+ (helpText ? '\n' + helpText : '')
)
};
}
+function certToPEM(cert) {
+ if (/-----BEGIN CERTIFICATE-----/.test(cert)) {
+ return cert;
+ }
+
+ cert = cert.match(/.{1,64}/g).join('\n');
+ cert = "-----BEGIN CERTIFICATE-----\n" + cert;
+ cert = cert + "\n-----END CERTIFICATE-----\n";
+ return cert;
+}
+
function getHashCode(str) {
var hash = 0;
if (str.length == 0) return hash;
@@ -110,6 +157,25 @@ function getHashCode(str) {
return hash;
}
+function getPath(path) {
+ if (path) {
+ return path.startsWith('/') ? path : '/' + path;
+ }
+}
+
+function getReqUrl(req, path) {
+ if (req) {
+ return (req.get('x-forwarded-proto') || req.protocol) + '://' + (req.get('x-forwarded-host') || req.get('host')) + getPath(path || req.originalUrl);
+ }
+};
+
+function removeHeaders(cert) {
+ const pem = /-----BEGIN (\w*)-----([^-]*)-----END (\w*)-----/g.exec(cert);
+ if (pem && pem.length > 0) {
+ return pem[2].replace(/[\n|\r\n]/g, '');
+ }
+ return cert;
+};
/**
* Arguments
@@ -129,137 +195,122 @@ function processArgs(args, options) {
}
return baseArgv
.usage('\nSimple IdP for SAML 2.0 WebSSO & SLO Profile\n\n' +
- 'Launches an IdP web server that mints SAML assertions or logout responses for a Service Provider (SP)\n\n' +
- 'Usage:\n\t$0 -acs {url} -aud {uri}')
+ 'Launches an IdP web server that mints SAML assertions or logout responses for a Service Provider (SP)\n\n' +
+ 'Usage:\n\t$0 -acs {url} -aud {uri}')
.options({
- port: {
+ idpPort: {
description: 'IdP Web Server Listener Port',
required: true,
- alias: 'p',
default: 7000
},
- cert: {
+ idpCert: {
description: 'IdP Signature PublicKey Certificate',
required: true,
default: './idp-public-cert.pem',
coerce: makeCertFileCoercer('certificate', 'IdP Signature PublicKey Certificate', KEY_CERT_HELP_TEXT)
},
- key: {
+ idpKey: {
description: 'IdP Signature PrivateKey Certificate',
required: true,
default: './idp-private-key.pem',
coerce: makeCertFileCoercer('RSA private key', 'IdP Signature PrivateKey Certificate', KEY_CERT_HELP_TEXT)
},
- issuer: {
+ idpIssuer: {
description: 'IdP Issuer URI',
required: true,
- alias: 'iss',
default: 'urn:example:idp'
},
- acsUrl: {
+ idpAcsUrl: {
description: 'SP Assertion Consumer URL',
required: true,
- alias: 'acs'
},
- sloUrl: {
+ idpSloUrl: {
description: 'SP Single Logout URL',
required: false,
- alias: 'slo'
},
- audience: {
+ idpAudience: {
description: 'SP Audience URI',
required: true,
- alias: 'aud'
},
- serviceProviderId: {
+ idpServiceProviderId: {
description: 'SP Issuer/Entity URI',
required: false,
- alias: 'spId',
string: true
},
- relayState: {
+ idpRelayState: {
description: 'Default SAML RelayState for SAMLResponse',
required: false,
- alias: 'rs'
},
- disableRequestAcsUrl: {
+ idpDisableRequestAcsUrl: {
description: 'Disables ability for SP AuthnRequest to specify Assertion Consumer URL',
required: false,
boolean: true,
- alias: 'static',
default: false
},
- encryptAssertion: {
+ idpEncryptAssertion: {
description: 'Encrypts assertion with SP Public Key',
required: false,
boolean: true,
- alias: 'enc',
default: false
},
- encryptionCert: {
+ idpEncryptionCert: {
description: 'SP Certificate (pem) for Assertion Encryption',
required: false,
string: true,
- alias: 'encCert',
coerce: makeCertFileCoercer('certificate', 'Encryption cert')
},
- encryptionPublicKey: {
+ idpEncryptionPublicKey: {
description: 'SP RSA Public Key (pem) for Assertion Encryption ' +
- '(e.g. openssl x509 -pubkey -noout -in sp-cert.pem)',
+ '(e.g. openssl x509 -pubkey -noout -in sp-cert.pem)',
required: false,
string: true,
- alias: 'encKey',
coerce: makeCertFileCoercer('public key', 'Encryption public key')
},
- httpsPrivateKey: {
+ idpHttpsPrivateKey: {
description: 'Web Server TLS/SSL Private Key (pem)',
required: false,
string: true,
coerce: makeCertFileCoercer('RSA private key')
},
- httpsCert: {
+ idpHttpsCert: {
description: 'Web Server TLS/SSL Certificate (pem)',
required: false,
string: true,
coerce: makeCertFileCoercer('certificate')
},
- https: {
+ idpHttps: {
description: 'Enables HTTPS Listener (requires httpsPrivateKey and httpsCert)',
required: true,
boolean: true,
default: false
},
- signResponse: {
+ idpSignResponse: {
description: 'Enables signing of responses',
required: false,
boolean: true,
default: true,
- alias: 'signResponse'
},
- configFile: {
+ idpConfigFile: {
description: 'Path to a SAML attribute config file',
required: true,
default: require.resolve('./config.js'),
- alias: 'conf'
},
- rollSession: {
+ idpRollSession: {
description: 'Create a new session for every authn request instead of reusing an existing session',
required: false,
boolean: true,
default: false
},
- authnContextClassRef: {
+ idpAuthnContextClassRef: {
description: 'Authentication Context Class Reference',
required: false,
string: true,
default: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport',
- alias: 'acr'
},
- authnContextDecl: {
+ idpAuthnContextDecl: {
description: 'Authentication Context Declaration (XML FilePath)',
required: false,
string: true,
- alias: 'acd',
coerce: function (value) {
const filePath = resolveFilePath(value);
if (filePath) {
@@ -271,499 +322,873 @@ function processArgs(args, options) {
description: 'IdP Base URL',
required: false,
string: true,
- alias: 'ibu'
+ },
+ spPort: {
+ description: 'Web Server listener port',
+ required: true,
+ number: true,
+ default: 7070
+ },
+ spProtocol: {
+ description: 'Federation Protocol',
+ required: true,
+ string: true,
+ default: 'samlp'
+ },
+ spIdpIssuer: {
+ description: 'IdP Issuer URI',
+ required: false,
+ string: true,
+ default: 'urn:example:idp'
+ },
+ spIdpSsoUrl: {
+ description: 'IdP Single Sign-On Service URL (SSO URL)',
+ required: false,
+ string: true
+ },
+ spIdpSsoBinding: {
+ description: 'IdP Single Sign-On AuthnRequest Binding',
+ required: true,
+ string: true,
+ default: BINDINGS.REDIRECT
+ },
+ spIdpSloUrl: {
+ description: 'IdP Single Logout Service URL (SLO URL) (SAMLP)',
+ required: false,
+ string: true
+ },
+ spIdpSloBinding: {
+ description: 'IdP Single Logout Request Binding (SAMLP)',
+ required: true,
+ string: true,
+ default: BINDINGS.REDIRECT
+ },
+ spIdpCert: {
+ description: 'IdP Public Key Signing Certificate (PEM)',
+ required: false,
+ string: true,
+ coerce: (value) => {
+ return certToPEM(makeCertFileCoercer('certificate', 'IdP Public Key Signing Certificate (PEM)', KEY_CERT_HELP_TEXT));
+ }
+ },
+ spIdpThumbprint: {
+ description: 'IdP Public Key Signing Certificate SHA1 Thumbprint',
+ required: false,
+ string: true,
+ coerce: (value) => {
+ return value ? value.replace(/:/g, '') : value
+ }
+ },
+ spIdpMetaUrl: {
+ description: 'IdP SAML Metadata URL',
+ required: false,
+ string: true
+ },
+ spAudience: {
+ description: 'SP Audience URI / RP Realm',
+ required: false,
+ string: true,
+ default: 'urn:example:sp'
+ },
+ spProviderName: {
+ description: 'SP Provider Name',
+ required: false,
+ string: true,
+ default: 'Simple SAML Service Provider'
+ },
+ spAcsUrls: {
+ description: 'SP Assertion Consumer Service (ACS) URLs (Relative URL)',
+ required: true,
+ array: true,
+ default: ['/saml/sso']
+ },
+ spSignAuthnRequests: {
+ description: 'Sign AuthnRequest Messages (SAMLP)',
+ required: true,
+ boolean: true,
+ default: true,
+ },
+ spSignatureAlgorithm: {
+ description: 'Signature Algorithm',
+ required: false,
+ string: true,
+ default: 'rsa-sha256'
+ },
+ spDigestAlgorithm: {
+ description: 'Digest Algorithm',
+ required: false,
+ string: true,
+ default: 'sha256'
+ },
+ spRequestNameIDFormat : {
+ description: 'Request Subject NameID Format (SAMLP)',
+ required: false,
+ boolean: true,
+ default: true
+ },
+ spValidateNameIDFormat : {
+ description: 'Validate format of Assertion Subject NameID',
+ required: false,
+ boolean: true,
+ default: true
+ },
+ spNameIDFormat : {
+ description: 'Assertion Subject NameID Format',
+ required: false,
+ string: true,
+ default: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
+ },
+ spRequestAuthnContext : {
+ description: 'Request Authentication Context (SAMLP)',
+ required: false,
+ boolean: true,
+ default: true
+ },
+ spAuthnContextClassRef : {
+ description: 'Authentication Context Class Reference',
+ required: false,
+ string: true,
+ default: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
+ },
+ spCert: {
+ description: 'SP/RP Public Key Signature & Encryption Certificate (PEM)',
+ string: true,
+ required: false,
+ default: path.resolve(__dirname, './sp-cert.pem'),
+ coerce: makeCertFileCoercer('certificate', 'SP Signing Public Key Certificate (PEM)', KEY_CERT_HELP_TEXT)
+ },
+ spKey: {
+ description: 'SP/RP Private Key Signature & Decryption Certificate(PEM)',
+ string: true,
+ required: false,
+ default: path.resolve(__dirname, './sp-key.pem'),
+ coerce: makeCertFileCoercer('privateKey', 'SP Signing Private Key (PEM)', KEY_CERT_HELP_TEXT)
+ },
+ spHttpsPrivateKey: {
+ description: 'Web Server TLS/SSL Private Key (PEM)',
+ required: false,
+ string: true,
+ coerce: makeCertFileCoercer('privateKey', 'Web Server TLS/SSL Private Key (PEM)', KEY_CERT_HELP_TEXT)
+ },
+ spHttpsCert: {
+ description: 'Web Server TLS/SSL Certificate (PEM)',
+ required: false,
+ string: true,
+ coerce: makeCertFileCoercer('certificate', 'Web Server TLS/SSL Public Key Certificate (PEM)', KEY_CERT_HELP_TEXT)
+ },
+ spHttps: {
+ description: 'Enables HTTPS Listener (requires httpsPrivateKey and httpsCert)',
+ required: false,
+ boolean: true,
+ default: false
+ },
+ spRelayState: {
+ description: 'Default Relay State',
+ required: false,
+ string: true
}
})
.example('\t$0 --acs http://acme.okta.com/auth/saml20/exampleidp --aud https://www.okta.com/saml2/service-provider/spf5aFRRXFGIMAYXQPNV', '')
.check(function(argv, aliases) {
- if (argv.encryptAssertion) {
- if (argv.encryptionPublicKey === undefined) {
+ if (argv.idpEncryptAssertion) {
+ if (argv.idpEncryptionPublicKey === undefined) {
return 'encryptionPublicKey argument is also required for assertion encryption';
}
- if (argv.encryptionCert === undefined) {
+ if (argv.idpEncryptionCert === undefined) {
return 'encryptionCert argument is also required for assertion encryption';
}
}
return true;
})
.check(function(argv, aliases) {
- if (argv.config) {
+ if (argv.idpConfig) {
return true;
}
- const configFilePath = resolveFilePath(argv.configFile);
+ const configFilePath = resolveFilePath(argv.idpConfigFile);
if (!configFilePath) {
- return 'SAML attribute config file path "' + argv.configFile + '" is not a valid path.\n';
+ return 'SAML attribute config file path "' + argv.idpConfigFile + '" is not a valid path.\n';
}
try {
- argv.config = require(configFilePath);
+ argv.idpConfig = require(configFilePath);
} catch (error) {
return 'Encountered an exception while loading SAML attribute config file "' + configFilePath + '".\n' + error;
}
return true;
})
+ .check((argv, aliases) => {
+ if (!_.isString(argv.spIdpMetaUrl)) {
+ if (!_.isString(argv.spIdpSsoUrl) || argv.spIdpSsoUrl === '') {
+ return 'IdP SSO Assertion Consumer URL (spIdpSsoUrl) is required when IdP metadata is not specified';
+ }
+ if (!_.isString(argv.spIdpCert) && !_.isString(argv.spIdpThumbprint)) {
+ return ' IdP Signing Certificate (spIdpCert) or IdP Signing Key Thumbprint (spIdpThumbprint) is required when IdP metadata is not specified';
+ }
+ // convert cert to PEM
+ argv.spIdpCertPEM = certToPEM(argv.spIdpCert)
+ }
+ return true;
+ })
.wrap(baseArgv.terminalWidth());
}
function _runServer(argv) {
- const app = express();
- const httpServer = argv.https ?
- https.createServer({ key: argv.httpsPrivateKey, cert: argv.httpsCert }, app) :
- http.createServer(app);
- const blocks = {};
+ IdPMetadata.fetch(argv.spIdpMetaUrl)
+ .then((metadata) => {
+ if (metadata.protocol) {
+ argv.protocol = metadata.protocol;
+ if (metadata.signingKeys[0]) {
+ argv.spIdpCert = certToPEM(metadata.signingKeys[0]);
+ }
- console.log();
- console.log('Listener Port:\n\t' + argv.port);
- console.log('HTTPS Enabled:\n\t' + argv.https);
- console.log();
- console.log('[IdP]');
- console.log();
- console.log('Issuer URI:\n\t' + argv.issuer);
- console.log('Sign Response Message:\n\t' + argv.signResponse);
- console.log('Encrypt Assertion:\n\t' + argv.encryptAssertion);
- console.log('Authentication Context Class Reference:\n\t' + argv.authnContextClassRef);
- console.log('Authentication Context Declaration:\n\n' + argv.authnContextDecl);
- console.log('Default RelayState:\n\t' + argv.relayState);
- console.log();
- console.log('[SP]');
- console.log();
- console.log('Issuer URI:\n\t' + argv.serviceProviderId);
- console.log('Audience URI:\n\t' + argv.audience);
- console.log('ACS URL:\n\t' + argv.acsUrl);
- console.log('SLO URL:\n\t' + argv.sloUrl);
- console.log('Trust ACS URL in Request:\n\t' + !argv.disableRequestAcsUrl);
- console.log();
+ switch (metadata.protocol) {
+ case 'samlp':
+ if (metadata.sso.redirectUrl) {
+ argv.spIdpSsoUrl = metadata.sso.redirectUrl;
+ } else if (metadata.sso.postUrl) {
+ argv.spIdpSsoUrl = metadata.sso.postUrl;
+ }
+ if (metadata.slo.redirectUrl) {
+ argv.spIdpSloUrl = metadata.slo.redirectUrl;
+ } else if (metadata.slo.postUrl) {
+ argv.spIdpSloUrl = metadata.slo.postUrl;
+ }
+ if (metadata.signRequest) {
+ argv.spSignAuthnRequests = metadata.signRequest;
+ }
+ break;
+ case 'wsfed':
+ if (metadata.sso.redirectUrl) {
+ argv.spIdpSsoUrl = metadata.sso.redirectUrl;
+ }
+ break;
+ }
+ }
+ })
+ .then(() => {
+ const app = express();
+ const httpServer = argv.idpHttps ?
+ https.createServer({ key: argv.idpHttpsPrivateKey, cert: argv.idpHttpsCert }, app) :
+ http.createServer(app);
+ const blocks = {};
- console.log();
+ console.log();
+ console.log('Listener Port:\n\t' + argv.idpPort);
+ console.log('HTTPS Enabled:\n\t' + argv.idpHttps);
+ console.log();
+ console.log('[IdP]');
+ console.log();
+ console.log('Issuer URI:\n\t' + argv.idpIssuer);
+ console.log('Sign Response Message:\n\t' + argv.idpSignResponse);
+ console.log('Encrypt Assertion:\n\t' + argv.idpEncryptAssertion);
+ console.log('Authentication Context Class Reference:\n\t' + argv.idpAuthnContextClassRef);
+ console.log('Authentication Context Declaration:\n\n' + argv.idpAuthnContextDecl);
+ console.log('Default RelayState:\n\t' + argv.idpRelayState);
+ console.log();
+ console.log('[IdP SP]');
+ console.log();
+ console.log('Issuer URI:\n\t' + argv.idpServiceProviderId);
+ console.log('Audience URI:\n\t' + argv.idpAudience);
+ console.log('ACS URL:\n\t' + argv.idpAcsUrl);
+ console.log('SLO URL:\n\t' + argv.idpSloUrl);
+ console.log('Trust ACS URL in Request:\n\t' + !argv.idpDisableRequestAcsUrl);
+ console.log();
+ console.log();
- /**
- * IdP Configuration
- */
-
- SimpleProfileMapper.prototype.metadata = argv.config.metadata;
-
- const idpOptions = {
- idpBaseUrl: argv.idpBaseUrl,
- issuer: argv.issuer,
- serviceProviderId: argv.serviceProviderId || argv.audience,
- cert: argv.cert,
- key: argv.key,
- audience: argv.audience,
- recipient: argv.acsUrl,
- destination: argv.acsUrl,
- acsUrl: argv.acsUrl,
- sloUrl: argv.sloUrl,
- RelayState: argv.relayState,
- allowRequestAcsUrl: !argv.disableRequestAcsUrl,
- digestAlgorithm: 'sha256',
- signatureAlgorithm: 'rsa-sha256',
- signResponse: argv.signResponse,
- encryptAssertion: argv.encryptAssertion,
- encryptionCert: argv.encryptionCert,
- encryptionPublicKey: argv.encryptionPublicKey,
- encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
- keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
- lifetimeInSeconds: 3600,
- authnContextClassRef: argv.authnContextClassRef,
- authnContextDecl: argv.authnContextDecl,
- includeAttributeNameFormat: true,
- profileMapper: SimpleProfileMapper,
- postEndpointPath: IDP_PATHS.SSO,
- redirectEndpointPath: IDP_PATHS.SSO,
- logoutEndpointPaths: argv.sloUrl ?
- {
- redirect: IDP_PATHS.SLO,
- post: IDP_PATHS.SLO
- } : {},
- getUserFromRequest: function(req) { return req.user; },
- getPostURL: function (audience, authnRequestDom, req, callback) {
- return callback(null, (req.authnRequest && req.authnRequest.acsUrl) ?
- req.authnRequest.acsUrl :
- argv.acsUrl);
- },
- transformAssertion: function(assertionDom) {
- if (argv.authnContextDecl) {
- var declDoc;
- try {
- declDoc = new Parser().parseFromString(argv.authnContextDecl);
- } catch(err){
- console.log('Unable to parse Authentication Context Declaration XML', err);
- }
- if (declDoc) {
- const authnContextDeclEl = assertionDom.createElementNS('urn:oasis:names:tc:SAML:2.0:assertion', 'saml:AuthnContextDecl');
- authnContextDeclEl.appendChild(declDoc.documentElement);
- const authnContextEl = assertionDom.getElementsByTagName('saml:AuthnContext')[0];
- authnContextEl.appendChild(authnContextDeclEl);
- }
- }
- },
- responseHandler: function(response, opts, req, res, next) {
- console.log();
- console.log(`Sending SAMLResponse to ${opts.postUrl} with RelayState ${opts.RelayState} =>\n`);
- console.log(xmlFormat(response.toString(), {indentation: ' '}));
- console.log();
- res.render('samlresponse', {
- AcsUrl: opts.postUrl,
- SAMLResponse: response.toString('base64'),
- RelayState: opts.RelayState
- });
- }
- }
+ console.log('[SP]');
+ console.log();
+ console.log('Protocol: ' + "SAMLP");
+ console.log();
+ console.log('IdP Issuer URI:\n\t' + argv.spIdpIssuer);
+ console.log('IdP SSO ACS URL:\n\t' + argv.spIdpSsoUrl);
+ console.log('IdP SLO URL:\n\t' + argv.spIdpSloUrl);
+ console.log();
+ console.log('SP Issuer URI:\n\t' + argv.spAudience);
+ console.log('SP Audience URI:\n\t' + argv.spAudience);
+ console.log('SP NameID Format:\n\t' + argv.spNameIDFormat);
+ console.log('SP ACS Binding:\n\turn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST');
+ console.log('SP ACS URL:');
+ argv.spAcsUrls.forEach(function(acsUrl) {
+ console.log('\t' + acsUrl);
+ });
+ console.log('SP Default Relay State:\n\t' + argv.spRelayState);
+ console.log();
- /**
- * App Environment
- */
+ /**
+ * IdP Configuration
+ */
+
+ SimpleProfileMapper.prototype.metadata = argv.idpConfig.metadata;
+
+ const idpOptions = {
+ idpBaseUrl: argv.idpBaseUrl,
+ issuer: argv.idpIssuer,
+ serviceProviderId: argv.idpServiceProviderId || argv.idpAudience,
+ cert: argv.idpCert,
+ key: argv.idpKey,
+ audience: argv.idpAudience,
+ recipient: argv.idpAcsUrl,
+ destination: argv.idpAcsUrl,
+ acsUrl: argv.idpAcsUrl,
+ sloUrl: argv.idpSloUrl,
+ RelayState: argv.idpRelayState,
+ allowRequestAcsUrl: !argv.idpDisableRequestAcsUrl,
+ digestAlgorithm: 'sha256',
+ signatureAlgorithm: 'rsa-sha256',
+ signResponse: argv.idpSignResponse,
+ encryptAssertion: argv.idpEncryptAssertion,
+ encryptionCert: argv.idpEncryptionCert,
+ encryptionPublicKey: argv.idpEncryptionPublicKey,
+ encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
+ keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
+ lifetimeInSeconds: 3600,
+ authnContextClassRef: argv.idpAuthnContextClassRef,
+ authnContextDecl: argv.idpAuthnContextDecl,
+ includeAttributeNameFormat: true,
+ profileMapper: SimpleProfileMapper,
+ postEndpointPath: IDP_PATHS.SSO,
+ redirectEndpointPath: IDP_PATHS.SSO,
+ logoutEndpointPaths: argv.idpSloUrl ?
+ {
+ redirect: IDP_PATHS.SLO,
+ post: IDP_PATHS.SLO
+ } : {},
+ getUserFromRequest: function(req) { return req.user; },
+ getPostURL: function (audience, authnRequestDom, req, callback) {
+ return callback(null, (req.authnRequest && req.authnRequest.acsUrl) ?
+ req.authnRequest.acsUrl :
+ argv.idpAcsUrl);
+ },
+ transformAssertion: function(assertionDom) {
+ if (argv.idpAuthnContextDecl) {
+ var declDoc;
+ try {
+ declDoc = new Parser().parseFromString(argv.idpAuthnContextDecl);
+ } catch(err){
+ console.log('Unable to parse Authentication Context Declaration XML', err);
+ }
+ if (declDoc) {
+ const authnContextDeclEl = assertionDom.createElementNS('urn:oasis:names:tc:SAML:2.0:assertion', 'saml:AuthnContextDecl');
+ authnContextDeclEl.appendChild(declDoc.documentElement);
+ const authnContextEl = assertionDom.getElementsByTagName('saml:AuthnContext')[0];
+ authnContextEl.appendChild(authnContextDeclEl);
+ }
+ }
+ },
+ responseHandler: function(response, opts, req, res, next) {
+ console.log();
+ console.log(`req.session.ssoResponse = ${JSON.stringify(req.session.ssoResponse)}\n`);
+ console.log(`Sending SAMLResponse to ${opts.postUrl} with RelayState ${opts.RelayState} =>\n`);
+ console.log(xmlFormat(response.toString(), {indentation: ' '}));
+ console.log();
+ res.render('samlresponse', {
+ AcsUrl: opts.postUrl,
+ SAMLResponse: response.toString('base64'),
+ RelayState: opts.RelayState
+ });
+ }
+ }
- app.set('port', process.env.PORT || argv.port);
- app.set('views', path.join(__dirname, 'views'));
+ const spConfig = {
+
+ port: argv.spPort,
+ protocol: argv.spProtocol,
+ idpIssuer: argv.spIdpIssuer,
+ idpSsoUrl: argv.spIdpSsoUrl,
+ idpSsoBinding: argv.spIdpSsoBinding,
+ idpSloUrl: argv.spIdpSloUrl,
+ idpSloBinding: argv.spIdpSloBinding,
+ idpCert: argv.spIdpCert,
+ idpThumbprint: argv.spIdpThumbprint,
+ idpMetaUrl: argv.spIdpMetaUrl,
+ audience: argv.spAudience,
+ providerName: argv.spProviderName,
+ acsUrls: argv.spAcsUrls,
+ signAuthnRequests: argv.spSignAuthnRequests,
+ signatureAlgorithm: argv.spSignatureAlgorithm,
+ digestAlgorithm: argv.spDigestAlgorithm,
+ requestNameIDFormat: argv.spRequestNameIDFormat,
+ validateNameIDFormat: argv.spValidateNameIDFormat,
+ nameIDFormat: argv.spNameIDFormat,
+ requestAuthnContext: argv.spRequestAuthnContext,
+ authnContextClassRef: argv.spAuthnContextClassRef,
+ spCert: argv.spCert,
+ spKey: argv.spKey,
+ httpsPrivateKey: argv.spHttpsPrivateKey,
+ httpsCert: argv.spHttpsCert,
+ https: argv.spHttps,
+ relayState: argv.spRelayState,
+
+ requestAcsUrl: argv.spAcsUrls[0],
+ failureRedirect: SP_ERROR_URL,
+ failureFlash: true,
+
+ // can't use arrow functions due to lexical scoping
+
+ getMetadataParams: function(req) {
+ return {
+ protocol: this.protocol,
+ entityID: this.audience,
+ realm: this.audience,
+ cert: removeHeaders(this.spCert),
+ acsUrls: this.acsUrls.map(url => getReqUrl(req, url)),
+ sloUrl: getReqUrl(req, SP_SLO_URL),
+ nameIDFormat: this.nameIDFormat
+ }
+ },
- /**
- * View Engine
- */
+ getRequestSecurityTokenParams: function(wreply, wctx) {
+ return {
+ wreply: wreply,
+ wctx: wctx || this.relayState,
+ }
+ },
+ getAuthnRequestParams: function(acsUrl, forceAuthn, relayState) {
+ const params = {
+ protocol: this.protocol,
+ realm: this.audience,
+ callback: acsUrl,
+ protocolBinding: this.idpSsoBinding,
+ identityProviderUrl: this.idpSsoUrl,
+ providerName: this.providerName,
+ forceAuthn: forceAuthn,
+ authnContext: this.authnContextClassRef,
+ requestContext: {
+ NameIDFormat: this.nameIDFormat
+ },
+ requestTemplate: AUTHN_REQUEST_TEMPLATE({
+ ForceAuthn: forceAuthn,
+ NameIDFormat: this.requestNameIDFormat,
+ AuthnContext: this.requestAuthnContext,
+ }),
+ signatureAlgorithm: this.signatureAlgorithm,
+ digestAlgorithm: this.digestAlgorithm,
+ deflate: this.deflate,
+ RelayState: relayState || this.relayState,
+ failureRedirect: this.failureRedirect,
+ failureFlash: this.failureFlash
+ }
- app.set('view engine', 'hbs');
- app.set('view options', { layout: 'layout' })
- app.engine('handlebars', hbs.__express);
- app.use('/samlproxy/idp/bower_components', express.static(__dirname + '/bower_components'))
+ if (this.signAuthnRequests) {
+ params.signingKey = {
+ cert: this.spCert,
+ key: this.spKey
+ }
+ }
+ return params;
+ },
+ getResponseParams: function(destinationUrl) {
+ return {
+ protocol: this.protocol,
+ thumbprint: this.idpThumbprint,
+ cert: removeHeaders(this.idpCert),
+ realm: this.audience,
+ identityProviderUrl: this.idpSsoUrl, //wsfed
+ recipientUrl: destinationUrl,
+ destinationUrl: destinationUrl,
+ decryptionKey: this.spKey,
+ checkResponseID: true,
+ checkDestination: true,
+ checkInResponseTo: true,
+ checkExpiration: true,
+ checkAudience: true,
+ checkNameQualifier: true,
+ checkSPNameQualifier: true,
+ failureRedirect: this.failureRedirect,
+ failureFlash: this.failureFlash
+ }
+ },
+
+ getLogoutParams: function() {
+ return {
+ issuer: this.audience,
+ protocolBinding: this.idpSloBinding,
+ deflate: this.deflate,
+ identityProviderUrl: this.idpSloUrl,
+ identityProviderSigningCert: this.idpCert,
+ key: this.spKey,
+ cert: this.spCert
+ }
+ }
- // Register Helpers
- hbs.registerHelper('extend', function(name, context) {
- var block = blocks[name];
- if (!block) {
- block = blocks[name] = [];
- }
+ };
- block.push(context.fn(this));
- });
+ responseParams = spConfig.getResponseParams();
+ const strategy = new SamlStrategy(responseParams,
+ (profile, done) => {
+ console.log();
+ console.log('Assertion => ' + JSON.stringify(profile, null, '\t'));
+ console.log();
+ return done(null, {
+ issuer: profile.issuer,
+ userName: profile.nameIdAttributes.value,
+ nameIdFormat: profile.nameIdAttributes.Format,
+ authnContext: {
+ sessionIndex: profile.sessionIndex,
+ authnMethod: profile['http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod']
+ },
+ claims: _.chain(profile)
+ .omit('issuer', 'sessionIndex', 'nameIdAttributes',
+ 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier',
+ 'http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod')
+ .value()
+ });
+
+ }
+ );
+ passport.use(strategy);
+
+ passport.serializeUser(function(user, done) {
+ done(null, user);
+ });
- hbs.registerHelper('block', function(name) {
- const val = (blocks[name] || []).join('\n');
- // clear the block
- blocks[name] = [];
- return val;
- });
+ passport.deserializeUser(function(user, done) {
+ done(null, user);
+ });
+ /**
+ * App Environment
+ */
+
+ app.set('port', process.env.PORT || argv.idpPort);
+ app.set('views', path.join(__dirname, 'views'));
+
+ /**
+ * View Engine
+ */
+
+ app.set('view engine', 'hbs');
+ app.set('view options', { layout: 'layout' })
+ app.engine('handlebars', hbs.__express);
+ app.use(express.static(path.join(__dirname, 'public')));
+ app.use('/samlproxy/idp/bower_components', express.static(__dirname + '/public/bower_components'))
+ app.use(passport.initialize());
+
+ // Register Helpers
+ hbs.registerHelper('extend', function(name, context) {
+ var block = blocks[name];
+ if (!block) {
+ block = blocks[name] = [];
+ }
- hbs.registerHelper('select', function(selected, options) {
- return options.fn(this).replace(
- new RegExp(' value=\"' + selected + '\"'), '$& selected="selected"');
- });
+ block.push(context.fn(this));
+ });
- hbs.registerHelper('getProperty', function(attribute, context) {
- return context[attribute];
- });
+ hbs.registerHelper('block', function(name) {
+ const val = (blocks[name] || []).join('\n');
+ // clear the block
+ blocks[name] = [];
+ return val;
+ });
- hbs.registerHelper('serialize', function(context) {
- return new Buffer(JSON.stringify(context)).toString('base64');
- });
- /**
- * Middleware
- */
+ hbs.registerHelper('select', function(selected, options) {
+ return options.fn(this).replace(
+ new RegExp(' value=\"' + selected + '\"'), '$& selected="selected"');
+ });
- app.use(logger(':date> :method :url - {:referrer} => :status (:response-time ms)', {
- skip: function (req, res)
- {
- return req.path.startsWith('/samlproxy/idp/bower_components') || req.path.startsWith('/samlproxy/idp/css')
- }
- }));
- app.use(bodyParser.urlencoded({extended: true}))
- app.use(cookieParser());
- app.use(express.static(path.join(__dirname, 'public')));
- app.use(session({
- secret: 'The universe works on a math equation that never even ever really ends in the end',
- resave: false,
- saveUninitialized: true,
- name: 'idp_sid',
- cookie: { maxAge: 60000 }
- }));
-
- /**
- * View Handlers
- */
-
- const showUser = function (req, res, next) {
- res.render('user', {
- user: req.user,
- participant: req.participant,
- metadata: req.metadata,
- authnRequest: req.authnRequest,
- idp: req.idp.options,
- paths: IDP_PATHS
- });
- }
+ hbs.registerHelper('getProperty', function(attribute, context) {
+ return context[attribute];
+ });
- /**
- * Shared Handlers
- */
+ hbs.registerHelper('serialize', function(context) {
+ return new Buffer(JSON.stringify(context)).toString('base64');
+ });
+
+ /**
+ * Middleware
+ */
+
+ app.use(logger(':date> :method :url - {:referrer} => :status (:response-time ms)', {
+ skip: function (req, res)
+ {
+ return req.path.startsWith('/samlproxy/idp/bower_components') || req.path.startsWith('/samlproxy/idp/css');
+ }
+ }));
+ app.use(bodyParser.urlencoded({extended: true}));
+ app.use(cookieParser());
+ app.use(session({
+ secret: 'The universe works on a math equation that never even ever really ends in the end',
+ resave: false,
+ saveUninitialized: true,
+ name: 'idp_sid',
+ cookie: { maxAge: 60000 }
+ }));
+
+ /**
+ * View Handlers
+ */
+
+ const showUser = function (req, res, next) {
+ const acsUrl = req.query.acsUrl ?
+ getReqUrl(req, req.query.acsUrl) :
+ getReqUrl(req, spConfig.requestAcsUrl);
+
+ params = spConfig.getAuthnRequestParams(
+ acsUrl,
+ req.query.forceauthn === '' || req.query.forceAuthn === '' || req.query.forceauthn || req.query.forceAuthn,
+ req.authnRequest.relayState
+ );
+ console.log('Generating SSO Request with Params ', params);
+ passport.authenticate('wsfed-saml2', params)(req, res, next);
+ };
- const parseSamlRequest = function(req, res, next) {
- samlp.parseRequest(req, function(err, data) {
- if (err) {
- return res.render('error', {
- message: 'SAML AuthnRequest Parse Error: ' + err.message,
- error: err
+ /**
+ * Shared Handlers
+ */
+
+ const parseSamlRequest = function(req, res, next) {
+ samlp.parseRequest(req, function(err, data) {
+ if (err) {
+ return res.render('error', {
+ message: 'SAML AuthnRequest Parse Error: ' + err.message,
+ error: err
+ });
+ };
+ if (data) {
+ req.authnRequest = {
+ relayState: req.query.RelayState || req.body.RelayState,
+ id: data.id,
+ issuer: data.issuer,
+ destination: data.destination,
+ acsUrl: data.assertionConsumerServiceURL,
+ forceAuthn: data.forceAuthn === 'true'
+ };
+ req.session.authnRequest = req.authnRequest;
+ console.log('Received AuthnRequest => \n', req.authnRequest);
+ }
+ return showUser(req, res, next);
});
};
- if (data) {
- req.authnRequest = {
- relayState: req.query.RelayState || req.body.RelayState,
- id: data.id,
- issuer: data.issuer,
- destination: data.destination,
- acsUrl: data.assertionConsumerServiceURL,
- forceAuthn: data.forceAuthn === 'true'
+
+ const getSessionIndex = function(req) {
+ if (req && req.session) {
+ return Math.abs(getHashCode(req.session.id)).toString();
+ }
+ };
+
+ const getParticipant = function(req) {
+ return {
+ serviceProviderId: req.idp.options.serviceProviderId,
+ sessionIndex: getSessionIndex(req),
+ nameId: req.user.userName,
+ nameIdFormat: req.user.nameIdFormat,
+ serviceProviderLogoutURL: req.idp.options.sloUrl
};
- console.log('Received AuthnRequest => \n', req.authnRequest);
- }
- return showUser(req, res, next);
- })
- };
+ };
- const getSessionIndex = function(req) {
- if (req && req.session) {
- return Math.abs(getHashCode(req.session.id)).toString();
- }
- }
+ const parseLogoutRequest = function(req, res, next) {
+ if (!req.idp.options.sloUrl) {
+ return res.render('error', {
+ message: 'SAML Single Logout Service URL not defined for Service Provider'
+ });
+ };
- const getParticipant = function(req) {
- return {
- serviceProviderId: req.idp.options.serviceProviderId,
- sessionIndex: getSessionIndex(req),
- nameId: req.user.userName,
- nameIdFormat: req.user.nameIdFormat,
- serviceProviderLogoutURL: req.idp.options.sloUrl
- }
- }
+ console.log('Processing SAML SLO request for participant => \n', req.participant);
+
+ return samlp.logout({
+ cert: req.idp.options.cert,
+ key: req.idp.options.key,
+ digestAlgorithm: req.idp.options.digestAlgorithm,
+ signatureAlgorithm: req.idp.options.signatureAlgorithm,
+ sessionParticipants: new SessionParticipants(
+ [
+ req.participant
+ ]),
+ clearIdPSession: function(callback) {
+ console.log('Destroying session ' + req.session.id + ' for participant', req.participant);
+ req.session.destroy();
+ callback();
+ }
+ })(req, res, next);
+ };
- const parseLogoutRequest = function(req, res, next) {
- if (!req.idp.options.sloUrl) {
- return res.render('error', {
- message: 'SAML Single Logout Service URL not defined for Service Provider'
+ /**
+ * Routes
+ */
+
+ app.use(function(req, res, next){
+ if (argv.idpRollSession) {
+ req.session.regenerate(function(err) {
+ return next();
+ });
+ } else {
+ next();
+ }
});
- };
-
- console.log('Processing SAML SLO request for participant => \n', req.participant);
-
- return samlp.logout({
- cert: req.idp.options.cert,
- key: req.idp.options.key,
- digestAlgorithm: req.idp.options.digestAlgorithm,
- signatureAlgorithm: req.idp.options.signatureAlgorithm,
- sessionParticipants: new SessionParticipants(
- [
- req.participant
- ]),
- clearIdPSession: function(callback) {
- console.log('Destroying session ' + req.session.id + ' for participant', req.participant);
- req.session.destroy();
- callback();
- }
- })(req, res, next);
- }
-
- /**
- * Routes
- */
- app.use(function(req, res, next){
- if (argv.rollSession) {
- req.session.regenerate(function(err) {
- return next();
+ app.use(function(req, res, next){
+ req.user = argv.idpConfig.user;
+ req.metadata = argv.idpConfig.metadata;
+ req.idp = { options: idpOptions };
+ req.participant = getParticipant(req);
+ next();
});
- } else {
- next()
- }
- });
- app.use(function(req, res, next){
- req.user = argv.config.user;
- req.metadata = argv.config.metadata;
- req.idp = { options: idpOptions };
- req.participant = getParticipant(req);
- next();
- });
+ app.get(['/', '/idp', IDP_PATHS.SSO], parseSamlRequest);
+ app.post(['/', '/idp', IDP_PATHS.SSO], parseSamlRequest);
+
+ app.get(IDP_PATHS.SLO, parseLogoutRequest);
+ app.post(IDP_PATHS.SLO, parseLogoutRequest);
+
+ app.post(IDP_PATHS.SIGN_IN, function(req, res) {
+ const authOptions = extend({}, req.idp.options);
+ Object.keys(req.body).forEach(function(key) {
+ var buffer;
+ if (key === '_authnRequest') {
+ buffer = new Buffer(req.body[key], 'base64');
+ req.authnRequest = JSON.parse(buffer.toString('utf8'));
+
+ // Apply AuthnRequest Params
+ authOptions.inResponseTo = req.authnRequest.id;
+ if (req.idp.options.allowRequestAcsUrl && req.authnRequest.acsUrl) {
+ authOptions.acsUrl = req.authnRequest.acsUrl;
+ authOptions.recipient = req.authnRequest.acsUrl;
+ authOptions.destination = req.authnRequest.acsUrl;
+ authOptions.forceAuthn = req.authnRequest.forceAuthn;
+ }
+ if (req.authnRequest.relayState) {
+ authOptions.RelayState = req.authnRequest.relayState;
+ }
+ } else {
+ req.user[key] = req.body[key];
+ }
+ });
- app.get(['/', '/idp', IDP_PATHS.SSO], parseSamlRequest);
- app.post(['/', '/idp', IDP_PATHS.SSO], parseSamlRequest);
-
- app.get(IDP_PATHS.SLO, parseLogoutRequest);
- app.post(IDP_PATHS.SLO, parseLogoutRequest);
-
- app.post(IDP_PATHS.SIGN_IN, function(req, res) {
- const authOptions = extend({}, req.idp.options);
- Object.keys(req.body).forEach(function(key) {
- var buffer;
- if (key === '_authnRequest') {
- buffer = new Buffer(req.body[key], 'base64');
- req.authnRequest = JSON.parse(buffer.toString('utf8'));
-
- // Apply AuthnRequest Params
- authOptions.inResponseTo = req.authnRequest.id;
- if (req.idp.options.allowRequestAcsUrl && req.authnRequest.acsUrl) {
- authOptions.acsUrl = req.authnRequest.acsUrl;
- authOptions.recipient = req.authnRequest.acsUrl;
- authOptions.destination = req.authnRequest.acsUrl;
- authOptions.forceAuthn = req.authnRequest.forceAuthn;
- }
- if (req.authnRequest.relayState) {
- authOptions.RelayState = req.authnRequest.relayState;
+ if (!authOptions.encryptAssertion) {
+ delete authOptions.encryptionCert;
+ delete authOptions.encryptionPublicKey;
}
- } else {
- req.user[key] = req.body[key];
- }
- });
- if (!authOptions.encryptAssertion) {
- delete authOptions.encryptionCert;
- delete authOptions.encryptionPublicKey;
- }
+ // Set Session Index
+ authOptions.sessionIndex = getSessionIndex(req);
- // Set Session Index
- authOptions.sessionIndex = getSessionIndex(req);
+ // Keep calm and Single Sign On
+ console.log('Sending SAML Response\nUser => \n%s\nOptions => \n',
+ JSON.stringify(req.user, null, 2), authOptions);
+ samlp.auth(authOptions)(req, res);
+ });
- // Keep calm and Single Sign On
- console.log('Sending SAML Response\nUser => \n%s\nOptions => \n',
- JSON.stringify(req.user, null, 2), authOptions);
- samlp.auth(authOptions)(req, res);
- })
+ app.get(IDP_PATHS.METADATA, function(req, res, next) {
+ samlp.metadata(req.idp.options)(req, res);
+ });
- app.get(IDP_PATHS.METADATA, function(req, res, next) {
- samlp.metadata(req.idp.options)(req, res);
- });
+ app.get(SP_METADATA_URL, function(req, res, next) {
+ const xml = METADATA_TEMPLATE(spConfig.getMetadataParams(req));
+ console.log(xml);
+ res.set('Content-Type', 'text/xml');
+ res.send(xml);
+ });
- app.post(IDP_PATHS.METADATA, function(req, res, next) {
- if (req.body && req.body.attributeName && req.body.displayName) {
- var attributeExists = false;
- const attribute = {
- id: req.body.attributeName,
- optional: true,
- displayName: req.body.displayName,
- description: req.body.description || '',
- multiValue: req.body.valueType === 'multi'
- };
+ spConfig.acsUrls.forEach(function(acsUrl) {
+ console.log(getPath(acsUrl));
+ app.get(getPath(acsUrl),
+ function (req, res, next) {
+ console.log(req.method);
+ console.log(req.query);
+ if (req.method === 'GET' && req.query && (req.query.SAMLResponse || req.body.wresult)) {
+ req.body = req.query;
+ const ssoResponse = {
+ state: req.query.RelayState || req.body.wctx,
+ url: getReqUrl(req, acsUrl)
+ };
+ req.session.ssoResponse = ssoResponse;
+ console.log();
+ console.log('Received SSO Response on ACS URL %s', ssoResponse.url);
+ console.log();
+
+ const params = spConfig.getResponseParams(ssoResponse.url);
+ console.log('Validating SSO Response with Params ', params);
+ _.extend(strategy.options, params);
+ passport.authenticate('wsfed-saml2', params)(req, res, next);
+ } else {
+ res.redirect(SP_LOGIN_URL);
+ }
+ },
+ function(req, res, next) {
+ const authOptions = extend({}, req.idp.options);
+ authOptions.RelayState = req.session.ssoResponse.state;
+ console.log('SP Sending SAML Response\nUser => \n%s\nOptions => \n',
+ JSON.stringify(req.user, null, 2), authOptions);
+ samlp.auth(authOptions)(req, res);
+ });
+ });
- req.metadata.forEach(function(entry) {
- if (entry.id === req.body.attributeName) {
- entry = attribute;
- attributeExists = true;
+ app.get(IDP_PATHS.SIGN_OUT, function(req, res, next) {
+ if (req.idp.options.sloUrl) {
+ console.log('Initiating SAML SLO request for user: ' + req.user.userName +
+ ' with sessionIndex: ' + getSessionIndex(req));
+ res.redirect(IDP_PATHS.SLO);
+ } else {
+ console.log('SAML SLO is not enabled for SP, destroying IDP session');
+ req.session.destroy(function(err) {
+ if (err) {
+ throw err;
+ }
+ res.redirect('back');
+ })
}
});
- if (!attributeExists) {
- req.metadata.push(attribute);
- }
- console.log("Updated SAML Attribute Metadata => \n", req.metadata)
- res.status(200).end();
- }
- });
+ // catch 404 and forward to error handler
+ app.use(function(req, res, next) {
+ const err = new Error('Route Not Found');
+ err.status = 404;
+ next(err);
+ });
- app.get(IDP_PATHS.SIGN_OUT, function(req, res, next) {
- if (req.idp.options.sloUrl) {
- console.log('Initiating SAML SLO request for user: ' + req.user.userName +
- ' with sessionIndex: ' + getSessionIndex(req));
- res.redirect(IDP_PATHS.SLO);
- } else {
- console.log('SAML SLO is not enabled for SP, destroying IDP session');
- req.session.destroy(function(err) {
+ // development error handler
+ app.use(function(err, req, res, next) {
if (err) {
- throw err;
+ res.status(err.status || 500);
+ res.render('error', {
+ message: err.message,
+ error: err
+ });
}
- res.redirect('back');
- })
- }
- });
-
- app.get([IDP_PATHS.SETTINGS], function(req, res, next) {
- res.render('settings', {
- idp: req.idp.options
- });
- });
-
- app.post([IDP_PATHS.SETTINGS], function(req, res, next) {
- Object.keys(req.body).forEach(function(key) {
- switch(req.body[key].toLowerCase()){
- case "true": case "yes": case "1":
- req.idp.options[key] = true;
- break;
- case "false": case "no": case "0":
- req.idp.options[key] = false;
- break;
- default:
- req.idp.options[key] = req.body[key];
- break;
- }
-
- if (req.body[key].match(/^\d+$/)) {
- req.idp.options[key] = parseInt(req.body[key], '10');
- }
- });
-
- console.log('Updated IdP Configuration => \n', req.idp.options);
- res.redirect('/');
- });
-
- // catch 404 and forward to error handler
- app.use(function(req, res, next) {
- const err = new Error('Route Not Found');
- err.status = 404;
- next(err);
- });
-
- // development error handler
- app.use(function(err, req, res, next) {
- if (err) {
- res.status(err.status || 500);
- res.render('error', {
- message: err.message,
- error: err
});
- }
- });
- /**
- * Start IdP Web Server
- */
+ /**
+ * Start IdP Web Server
+ */
- console.log('starting idp server on port %s', app.get('port'));
+ console.log('starting idp server on port %s', app.get('port'));
- httpServer.listen(app.get('port'), function() {
- const scheme = argv.https ? 'https' : 'http',
- address = httpServer.address(),
- hostname = os.hostname();
+ httpServer.listen(app.get('port'), function() {
+ const scheme = argv.idpHttps ? 'https' : 'http',
+ address = httpServer.address(),
+ hostname = os.hostname();
baseUrl = address.address === '0.0.0.0' || address.address === '::' ?
scheme + '://' + hostname + ':' + address.port :
scheme + '://localhost:' + address.port;
- /*
- * baseUrl = argv.idpBaseUrl ? argv.idpBaseUrl : baseUrl;
-*/
-
- console.log();
- console.log('SAML IdP Metadata URL: ');
- console.log('\t=> ' + baseUrl + IDP_PATHS.METADATA);
- console.log();
- console.log('SSO Bindings: ');
- console.log();
- console.log('\turn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST');
- console.log('\t\t=> ' + baseUrl + IDP_PATHS.SSO);
- console.log('\turn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect');
- console.log('\t\t=> ' + baseUrl + IDP_PATHS.SSO);
- console.log();
- if (argv.sloUrl) {
- console.log('SLO Bindings: ');
- console.log();
- console.log('\turn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST');
- console.log('\t\t=> ' + baseUrl + IDP_PATHS.SLO);
- console.log('\turn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect');
- console.log('\t\t=> ' + baseUrl + IDP_PATHS.SLO);
- console.log();
- }
- console.log('idp server ready');
- console.log('\t=> ' + baseUrl);
- console.log();
- });
+
+ console.log();
+ console.log('SAML IdP Metadata URL: ');
+ console.log('\t=> ' + baseUrl + IDP_PATHS.METADATA);
+ console.log();
+ console.log('SSO Bindings: ');
+ console.log();
+ console.log('\turn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST');
+ console.log('\t\t=> ' + baseUrl + IDP_PATHS.SSO);
+ console.log('\turn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect');
+ console.log('\t\t=> ' + baseUrl + IDP_PATHS.SSO);
+ console.log();
+ if (argv.idpSloUrl) {
+ console.log('SLO Bindings: ');
+ console.log();
+ console.log('\turn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST');
+ console.log('\t\t=> ' + baseUrl + IDP_PATHS.SLO);
+ console.log('\turn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect');
+ console.log('\t\t=> ' + baseUrl + IDP_PATHS.SLO);
+ console.log();
+ }
+ console.log('idp server ready');
+ console.log('\t=> ' + baseUrl);
+ console.log();
+ });
+ });
}
function runServer(options) {
diff --git a/bower.json b/bower.json
index b8cbbbc40..4acee3572 100644
--- a/bower.json
+++ b/bower.json
@@ -1,5 +1,5 @@
{
- "name": "saml-idp",
+ "name": "saml-proxy",
"private": true,
"dependencies": {
"bootstrap-tagsinput": "0.8.0",
@@ -10,11 +10,11 @@
"jquery-validation": "1.17.0"
},
"authors": [
- "Karl McGuinness"
+ "Patrick Vinograd"
],
"repository": {
"type": "git",
- "url": "https://github.com/mcguinness/saml-idp"
+ "url": "https://github.com/patrickvinograd/saml-proxy"
},
"license": "MIT",
"ignore": [
diff --git a/config.js b/config.js
index f0247a5ec..751db5c81 100644
--- a/config.js
+++ b/config.js
@@ -3,61 +3,81 @@
* User Profile
*/
var profile = {
- userName: 'saml.jackson@example.com',
- nameIdFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
- firstName: 'Saml',
- lastName: 'Jackson',
- displayName: 'saml jackson',
- email: 'saml.jackson@example.com',
- mobilePhone: '+1-415-555-5141',
- groups: 'Simple IdP Users, West Coast Users, Cloud Users'
+ birth_date: '1936-04-10',
+ email: 'vets.gov.user+503@id.me',
+ fname: 'Wendeline',
+ social: '564930708',
+ gender: 'Female',
+ lname: 'O\'Heffernan',
+ level_of_assurance: '3',
+ mname: 'Kitty',
+ multifactor: 'true',
+ uuid: '43bb64d44a44452a8b30929003a89f53'
}
/**
* SAML Attribute Metadata
*/
var metadata = [{
- id: "firstName",
+ id: "fname",
optional: false,
displayName: 'First Name',
- description: 'The given name of the user',
+ description: 'The given name of the Veteran',
multiValue: false
}, {
- id: "lastName",
+ id: "lname",
optional: false,
displayName: 'Last Name',
- description: 'The surname of the user',
+ description: 'The surname of the Veteran',
multiValue: false
}, {
- id: "displayName",
+ id: "mname",
optional: true,
- displayName: 'Display Name',
- description: 'The display name of the user',
+ displayName: 'Middle Name',
+ description: 'The middle name of the Veteran',
multiValue: false
}, {
id: "email",
optional: false,
displayName: 'E-Mail Address',
- description: 'The e-mail address of the user',
+ description: 'The e-mail address of the Veteran',
multiValue: false
},{
- id: "mobilePhone",
+ id: "social",
optional: true,
- displayName: 'Mobile Phone',
- description: 'The mobile phone of the user',
+ displayName: 'SSN',
+ description: 'The SSN of the Veteran',
multiValue: false
}, {
- id: "groups",
+ id: "multifactor",
optional: true,
- displayName: 'Groups',
- description: 'Group memberships of the user',
- multiValue: true
+ displayName: 'Multifactor',
+ description: 'If the Veteran has two factor auth enabled',
+ multiValue: false
+}, {
+ id: "gender",
+ optional: true,
+ displayName: 'Gender',
+ description: 'The gender of the Veteran',
+ multiValue: false
}, {
- id: "userType",
+ id: "uuid",
optional: true,
- displayName: 'User Type',
- description: 'The type of user',
- options: ['Admin', 'Editor', 'Commenter']
+ displayName: 'uuid',
+ description: 'UUID of the Veteran model',
+ multiValue: false
+}, {
+ id: "level_of_assurance",
+ optional: true,
+ displayName: 'Level of Assurance',
+ description: 'Level of identify proofing available for the Veteran',
+ multiValue: false
+}, {
+ id: "birth_date",
+ optional: false,
+ displayName: 'Birth Date',
+ description: 'The birth date of the Veteran',
+ multiValue: false
}];
module.exports = {
diff --git a/docker-compose.yml b/docker-compose.yml
index ffc171b90..56cb19caa 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,4 @@
-saml-idp:
+saml-proxy:
build: .
ports:
- "7000:7000"
diff --git a/idp-metadata.js b/idp-metadata.js
new file mode 100644
index 000000000..dbebea42a
--- /dev/null
+++ b/idp-metadata.js
@@ -0,0 +1,119 @@
+'use strict';
+
+const util = require('util'),
+ request = require("request"),
+ xml2js = require('xml2js');
+
+function getBindingLocation(serviceEl, bindingUri) {
+ var location;
+ if (serviceEl && serviceEl.length > 0) {
+ serviceEl.forEach((element, index, array) => {
+ if (element.$.Binding.toLowerCase() === bindingUri) {
+ location = element.$.Location;
+ }
+ });
+ }
+ return location;
+};
+
+function getFirstCert(keyEl) {
+ if (keyEl.KeyInfo &&
+ keyEl.KeyInfo.length === 1,
+ keyEl.KeyInfo[0].X509Data &&
+ keyEl.KeyInfo[0].X509Data.length === 1,
+ keyEl.KeyInfo[0].X509Data[0].X509Certificate &&
+ keyEl.KeyInfo[0].X509Data[0].X509Certificate.length === 1) {
+
+ return keyEl.KeyInfo[0].X509Data[0].X509Certificate[0]._;
+ }
+}
+
+function fetch(url) {
+
+ return new Promise((resolve, reject) => {
+ const metadata = { sso: {}, slo: {}, nameIdFormats: [], signingKeys: [] };
+
+ if (typeof url === 'undefined' || url === null) {
+ return resolve(metadata);
+ }
+
+ console.log('downloading IdP metadata from ' + url)
+ request.get(url, (err, resp, body) => {
+ if (err) {
+ console.log('unable to fetch metadata: ' + err.message);
+ return reject(err);
+ };
+
+ console.log();
+ console.log(body);
+ console.log();
+
+ const parserConfig = {
+ explicitRoot: true,
+ explicitCharkey: true,
+ tagNameProcessors: [xml2js.processors.stripPrefix]
+ },
+ parser = new xml2js.Parser(parserConfig),
+ nameIds = [];
+
+ parser.parseString(body, (err, docEl) => {
+ if (err) {
+ return reject(err);
+ }
+
+ if (docEl.EntityDescriptor) {
+ metadata.issuer = docEl.EntityDescriptor.$.entityID
+
+ if (docEl.EntityDescriptor.IDPSSODescriptor && docEl.EntityDescriptor.IDPSSODescriptor.length === 1) {
+
+ metadata.protocol = 'samlp';
+
+ let ssoEl = docEl.EntityDescriptor.IDPSSODescriptor[0];
+ metadata.signRequest = ssoEl.$.WantAuthnRequestsSigned;
+
+ ssoEl.KeyDescriptor.forEach((keyEl) => {
+ if (keyEl.$.use && keyEl.$.use.toLowerCase() !== 'encryption') {
+ metadata.signingKeys.push(getFirstCert(keyEl));
+ }
+ })
+
+ if (ssoEl.NameIDFormat) {
+ ssoEl.NameIDFormat.forEach((element, index, array) => {
+ if (element._) {
+ metadata.nameIdFormats.push(element._);
+ }
+ });
+ }
+
+ metadata.sso.redirectUrl = getBindingLocation(ssoEl.SingleSignOnService, 'urn:oasis:names:tc:saml:2.0:bindings:http-redirect');
+ metadata.sso.postUrl = getBindingLocation(ssoEl.SingleSignOnService, 'urn:oasis:names:tc:saml:2.0:bindings:http-post');
+
+ metadata.slo.redirectUrl = getBindingLocation(ssoEl.SingleLogoutService, 'urn:oasis:names:tc:saml:2.0:bindings:http-redirect');
+ metadata.slo.postUrl = getBindingLocation(ssoEl.SingleLogoutService, 'urn:oasis:names:tc:saml:2.0:bindings:http-post');
+ }
+ }
+
+ if (docEl.EntityDescriptor.RoleDescriptor) {
+ metadata.protocol = 'wsfed';
+ try {
+ let roleEl = docEl.EntityDescriptor.RoleDescriptor.find((el) => {
+ return el.$['xsi:type'].endsWith(':SecurityTokenServiceType');
+ });
+ metadata.sso.redirectUrl = roleEl.PassiveRequestorEndpoint[0].EndpointReference[0].Address[0]._
+
+ roleEl.KeyDescriptor.forEach((keyEl) => {
+ metadata.signingKeys.push(getFirstCert(keyEl));
+ })
+ } catch(e) {
+ console.log('unable to parse RoleDescriptor metadata', e);
+ }
+ }
+ return resolve(metadata);
+ });
+ });
+ });
+}
+
+module.exports = {
+ fetch: fetch
+};
diff --git a/lib/simpleProfileMapper.js b/lib/simpleProfileMapper.js
index e6e66eab3..ff41865cd 100644
--- a/lib/simpleProfileMapper.js
+++ b/lib/simpleProfileMapper.js
@@ -11,10 +11,11 @@ SimpleProfileMapper.prototype.getClaims = function() {
SimpleProfileMapper.prototype.metadata.forEach(function(entry) {
claims[entry.id] = entry.multiValue ?
- self._pu[entry.id].split(',') :
- self._pu[entry.id];
+ self._pu['claims'][entry.id].split(',') :
+ self._pu['claims'][entry.id];
});
+ console.log(claims);
return Object.keys(claims).length && claims;
};
@@ -67,4 +68,4 @@ SimpleProfileMapper.prototype.metadata = [ {
multiValue: true
}];
-module.exports = SimpleProfileMapper;
\ No newline at end of file
+module.exports = SimpleProfileMapper;
diff --git a/package-lock.json b/package-lock.json
index c89a870c6..7d860fed9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "saml-idp",
- "version": "1.0.1",
+ "version": "1.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -18,6 +18,17 @@
"negotiator": "0.6.1"
}
},
+ "ajv": {
+ "version": "5.5.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+ "requires": {
+ "co": "^4.6.0",
+ "fast-deep-equal": "^1.0.0",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.3.0"
+ }
+ },
"align-text": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
@@ -43,11 +54,36 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
+ "asn1": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+ "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+ },
"async": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
},
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+ },
+ "aws4": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
+ "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w=="
+ },
"basic-auth": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
@@ -56,6 +92,15 @@
"safe-buffer": "5.1.1"
}
},
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "optional": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
@@ -83,12 +128,25 @@
}
}
},
+ "boom": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz",
+ "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=",
+ "requires": {
+ "hoek": "0.9.x"
+ }
+ },
"bower": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/bower/-/bower-1.8.4.tgz",
"integrity": "sha1-54dqB23rgTf30GUl3F6MZtuC8oo=",
"dev": true
},
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+ },
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@@ -100,6 +158,11 @@
"integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
"optional": true
},
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+ },
"center-align": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
@@ -134,11 +197,24 @@
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
},
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+ },
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
+ "combined-stream": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
+ "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
"content-disposition": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
@@ -168,6 +244,11 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
"crc": {
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz",
@@ -183,6 +264,22 @@
"which": "^1.2.9"
}
},
+ "cryptiles": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz",
+ "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=",
+ "requires": {
+ "boom": "0.4.x"
+ }
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
@@ -196,6 +293,11 @@
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+ },
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
@@ -206,6 +308,23 @@
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
+ "ecc-jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
+ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
+ "optional": true,
+ "requires": {
+ "jsbn": "~0.1.0"
+ }
+ },
+ "ecdsa-sig-formatter": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz",
+ "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -389,6 +508,21 @@
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
},
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+ },
+ "fast-deep-equal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+ },
"finalhandler": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
@@ -436,11 +570,31 @@
"utils-flatten": "^1.0.0"
}
},
+ "foreach": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
+ },
"foreachasync": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz",
"integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY="
},
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+ },
+ "form-data": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
+ "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -461,6 +615,14 @@
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
"handlebars": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.5.tgz",
@@ -472,6 +634,20 @@
"uglify-js": "^2.6"
}
},
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+ },
+ "har-validator": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
+ "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
+ "requires": {
+ "ajv": "^5.1.0",
+ "har-schema": "^2.0.0"
+ }
+ },
"hbs": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/hbs/-/hbs-4.0.1.tgz",
@@ -481,6 +657,11 @@
"walk": "2.3.9"
}
},
+ "hoek": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz",
+ "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU="
+ },
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
@@ -492,6 +673,16 @@
"statuses": ">= 1.4.0 < 2"
}
},
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
@@ -500,6 +691,11 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
+ "indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+ },
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
@@ -515,6 +711,11 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz",
"integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs="
},
+ "is": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/is/-/is-0.2.7.tgz",
+ "integrity": "sha1-OzSixI81mXLzUEKEkZOucmS2NWI="
+ },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@@ -525,16 +726,90 @@
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
+ "is-object": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/is-object/-/is-object-0.1.2.tgz",
+ "integrity": "sha1-AO+8CIFsM8/ErIJR0TLhDcZQmNc="
+ },
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+ },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "optional": true
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+ },
+ "json-schema-traverse": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+ },
+ "jsonwebtoken": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-5.0.5.tgz",
+ "integrity": "sha1-ZZLMBe4D3VrZ4DqRCRGk2nmv4Pg=",
+ "requires": {
+ "jws": "^3.0.0"
+ }
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "jwa": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz",
+ "integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.10",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz",
+ "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==",
+ "requires": {
+ "jwa": "^1.1.5",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -691,6 +966,21 @@
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
+ "oauth-sign": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
+ },
+ "object-keys": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.2.0.tgz",
+ "integrity": "sha1-zd7AKZiwkb5CvxA1rjLknxy26mc=",
+ "requires": {
+ "foreach": "~2.0.1",
+ "indexof": "~0.0.1",
+ "is": "~0.2.6"
+ }
+ },
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@@ -754,6 +1044,52 @@
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
},
+ "passport": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz",
+ "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=",
+ "requires": {
+ "passport-strategy": "1.x.x",
+ "pause": "0.0.1"
+ }
+ },
+ "passport-strategy": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+ "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
+ },
+ "passport-wsfed-saml2": {
+ "version": "github:edpaget/passport-wsfed-saml2#2667ba83974af3fc1dba75ed3a62faaf04e8aa06",
+ "from": "github:edpaget/passport-wsfed-saml2#auth-demo",
+ "requires": {
+ "cryptiles": "~0.2.2",
+ "ejs": "2.5.5",
+ "jsonwebtoken": "~5.0.4",
+ "passport-strategy": "^1.0.0",
+ "uid2": "0.0.x",
+ "valid-url": "^1.0.9",
+ "xml-crypto": "github:auth0/xml-crypto#65fa1e22c5884e45135d0bacccb99ad11e900bf1",
+ "xml-encryption": "0.11.0",
+ "xml2js": "0.1.x",
+ "xmldom": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392",
+ "xpath": "0.0.5",
+ "xtend": "~2.0.3"
+ },
+ "dependencies": {
+ "xml2js": {
+ "version": "0.1.14",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.1.14.tgz",
+ "integrity": "sha1-UnTmf1pkxfkpdM2FE54DMq3GuQw=",
+ "requires": {
+ "sax": ">=0.1.1"
+ }
+ },
+ "xmldom": {
+ "version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392",
+ "from": "github:auth0/xmldom#v0.1.19-auth0_1"
+ }
+ }
+ },
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
@@ -769,6 +1105,16 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
+ "pause": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+ "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+ },
"proxy-addr": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
@@ -783,6 +1129,11 @@
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+ },
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
@@ -819,6 +1170,33 @@
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
"integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
},
+ "request": {
+ "version": "2.87.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz",
+ "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==",
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.6.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.5",
+ "extend": "~3.0.1",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.1",
+ "har-validator": "~5.0.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.17",
+ "oauth-sign": "~0.8.2",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.1",
+ "safe-buffer": "^5.1.1",
+ "tough-cookie": "~2.3.3",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.1.0"
+ }
+ },
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -875,8 +1253,8 @@
}
},
"samlp": {
- "version": "github:mcguinness/node-samlp#e6c5e3ed4c02546e0b4008a339173a71d86b602f",
- "from": "github:mcguinness/node-samlp",
+ "version": "github:edpaget/node-samlp#4e93a51feaae42fe69d32592dc1452fbcd94d53b",
+ "from": "github:edpaget/node-samlp#auth-demo",
"requires": {
"@auth0/thumbprint": "0.0.6",
"ejs": "2.5.5",
@@ -892,9 +1270,19 @@
"xmldom": {
"version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392",
"from": "github:auth0/xmldom#v0.1.19-auth0_1"
+ },
+ "xtend": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-1.0.3.tgz",
+ "integrity": "sha1-P12Tc1PM7Y4IU5mlY/2yJUHClgo="
}
}
},
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+ },
"send": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
@@ -977,6 +1365,22 @@
"amdefine": ">=0.0.4"
}
},
+ "sshpk": {
+ "version": "1.14.2",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
+ "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ }
+ },
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@@ -1004,6 +1408,28 @@
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
},
+ "tough-cookie": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
+ "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
+ "requires": {
+ "punycode": "^1.4.1"
+ }
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "optional": true
+ },
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
@@ -1058,6 +1484,16 @@
"random-bytes": "~1.0.0"
}
},
+ "uid2": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
+ "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I="
+ },
+ "underscore": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz",
+ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg=="
+ },
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -1073,6 +1509,11 @@
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
+ "uuid": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
+ },
"valid-url": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
@@ -1083,6 +1524,16 @@
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
"walk": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/walk/-/walk-2.3.9.tgz",
@@ -1158,9 +1609,8 @@
}
},
"xml-crypto": {
- "version": "0.10.1",
- "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-0.10.1.tgz",
- "integrity": "sha1-+DL3TM9W8kr8rhFjofyrRNlndKg=",
+ "version": "github:auth0/xml-crypto#65fa1e22c5884e45135d0bacccb99ad11e900bf1",
+ "from": "github:auth0/xml-crypto#fix-digest",
"requires": {
"xmldom": "=0.1.19",
"xpath.js": ">=0.0.3"
@@ -1236,6 +1686,20 @@
}
}
},
+ "xml2js": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
+ "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~9.0.1"
+ }
+ },
+ "xmlbuilder": {
+ "version": "9.0.7",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+ "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
+ },
"xmldom": {
"version": "0.1.27",
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz",
@@ -1252,9 +1716,13 @@
"integrity": "sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ=="
},
"xtend": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-1.0.3.tgz",
- "integrity": "sha1-P12Tc1PM7Y4IU5mlY/2yJUHClgo="
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.0.6.tgz",
+ "integrity": "sha1-XqZXptukRwacLlnFihE4ywxebO4=",
+ "requires": {
+ "is-object": "~0.1.2",
+ "object-keys": "~0.2.0"
+ }
},
"y18n": {
"version": "3.2.1",
diff --git a/package.json b/package.json
index 94c4a4b20..b0b9846c8 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,9 @@
{
- "name": "saml-idp",
- "description": "Test Identity Provider (IdP) for SAML 2.0 Web Browser SSO Profile",
+ "name": "saml-proxy",
+ "description": "SAML 2.0 Proxy allows modification/mediation of requests/responses",
"version": "1.1.0",
"private": false,
- "author": "Karl McGuinness",
+ "author": "Patrick Vinograd",
"keywords": [
"saml",
"idp",
@@ -12,10 +12,10 @@
"license": "MIT",
"repository": {
"type": "git",
- "url": "https://github.com/mcguinness/saml-idp"
+ "url": "https://github.com/patrickvinograd/saml-proxy"
},
"bugs": {
- "url": "https://github.com/mcguinness/saml-idp/issues"
+ "url": "https://github.com/patrickvinograd/saml-proxy/issues"
},
"main": "./app.js",
"scripts": {
@@ -35,9 +35,14 @@
"extend": "^3.0.1",
"hbs": "~4.0.1",
"morgan": "~1.9.0",
- "samlp": "mcguinness/node-samlp",
+ "passport": "^0.4.0",
+ "passport-wsfed-saml2": "edpaget/passport-wsfed-saml2#auth-demo",
+ "samlp": "edpaget/node-samlp#auth-demo",
+ "underscore": "^1.9.0",
"xml-formatter": "^1.0.1",
"xmldom": "^0.1.27",
+ "request": "^2.87.0",
+ "xml2js": "^0.4.19",
"yargs": "^11.0.0"
},
"devDependencies": {
diff --git a/templates/authnrequest.tpl b/templates/authnrequest.tpl
new file mode 100644
index 000000000..ac0f18562
--- /dev/null
+++ b/templates/authnrequest.tpl
@@ -0,0 +1,7 @@
+