From 01da0a656a507436b3a35a2868ec93416b9e2688 Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Mon, 30 Jul 2018 19:35:25 -0700 Subject: [PATCH 01/15] Attempted combined idp-sp proxy service --- app.js | 666 ++++++++++++++++++++++------- idp-metadata.js | 119 ++++++ package-lock.json | 831 +++++++++++++++++++++++++++++-------- package.json | 5 + templates/authnrequest.tpl | 7 + templates/metadata.tpl | 15 + 6 files changed, 1310 insertions(+), 333 deletions(-) create mode 100644 idp-metadata.js create mode 100644 templates/authnrequest.tpl create mode 100644 templates/metadata.tpl diff --git a/app.js b/app.js index d13524c1e..46392e67a 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,13 @@ const express = require('express'), yargs = require('yargs/yargs'), xmlFormat = require('xml-formatter'), samlp = require('samlp'), + SamlStrategy = require('passport-wsfed-saml2').Strategy, + 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,6 +40,36 @@ 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-Redirect' +} + +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-----/, @@ -99,6 +134,17 @@ function makeCertFileCoercer(type, description, 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 +156,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 @@ -132,134 +197,119 @@ function processArgs(args, options) { '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)', 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,108 +321,339 @@ 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) { + IdPMetadata.fetch(argv.spIdpMetaUrl) + .then((metadata) => { + if (metadata.protocol) { + argv.protocol = metadata.protocol; + if (metadata.signingKeys[0]) { + argv.spIdpCert = certToPEM(metadata.signingKeys[0]); + } + + switch (metadata.protocol) { + case 'samlp': + if (metadata.sso.redirectUrl) { + argv.spIdpSsoUrl = metadata.sso.redirectUrl; + argv.spIdpSsoBinding = BINDINGS.REDIRECT + } else if (metadata.sso.postUrl) { + argv.spIdpSsoUrl = metadata.sso.postUrl; + argv.spIdpSsoBinding = BINDINGS.POST + } + if (metadata.slo.redirectUrl) { + argv.spIdpSloUrl = metadata.slo.redirectUrl; + argv.spIdpSloBinding = BINDINGS.REDIRECT + } else if (metadata.slo.postUrl) { + argv.spIdpSloUrl = metadata.slo.postUrl; + argv.spIdpSloBinding = BINDINGS.POST + } + 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.https ? - https.createServer({ key: argv.httpsPrivateKey, cert: argv.httpsCert }, app) : + const httpServer = argv.idpHttps ? + https.createServer({ key: argv.idpHttpsPrivateKey, cert: argv.idpHttpsCert }, app) : http.createServer(app); const blocks = {}; console.log(); - console.log('Listener Port:\n\t' + argv.port); - console.log('HTTPS Enabled:\n\t' + argv.https); + 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.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('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('[SP]'); + console.log('[IdP 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('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(); + 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(); /** * IdP Configuration */ - SimpleProfileMapper.prototype.metadata = argv.config.metadata; + SimpleProfileMapper.prototype.metadata = argv.idpConfig.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, + 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.signResponse, - encryptAssertion: argv.encryptAssertion, - encryptionCert: argv.encryptionCert, - encryptionPublicKey: argv.encryptionPublicKey, + 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.authnContextClassRef, - authnContextDecl: argv.authnContextDecl, + authnContextClassRef: argv.idpAuthnContextClassRef, + authnContextDecl: argv.idpAuthnContextDecl, includeAttributeNameFormat: true, profileMapper: SimpleProfileMapper, postEndpointPath: IDP_PATHS.SSO, redirectEndpointPath: IDP_PATHS.SSO, - logoutEndpointPaths: argv.sloUrl ? + logoutEndpointPaths: argv.idpSloUrl ? { redirect: IDP_PATHS.SLO, post: IDP_PATHS.SLO @@ -381,13 +662,13 @@ function _runServer(argv) { getPostURL: function (audience, authnRequestDom, req, callback) { return callback(null, (req.authnRequest && req.authnRequest.acsUrl) ? req.authnRequest.acsUrl : - argv.acsUrl); + argv.idpAcsUrl); }, transformAssertion: function(assertionDom) { - if (argv.authnContextDecl) { + if (argv.idpAuthnContextDecl) { var declDoc; try { - declDoc = new Parser().parseFromString(argv.authnContextDecl); + declDoc = new Parser().parseFromString(argv.idpAuthnContextDecl); } catch(err){ console.log('Unable to parse Authentication Context Declaration XML', err); } @@ -412,11 +693,136 @@ function _runServer(argv) { } } + 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, SLO_URL), + nameIDFormat: this.nameIDFormat + } + }, + + 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 + } + + 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 + } + } + + }; + + /** * App Environment */ - app.set('port', process.env.PORT || argv.port); + app.set('port', process.env.PORT || argv.idpPort); app.set('views', path.join(__dirname, 'views')); /** @@ -485,14 +891,22 @@ function _runServer(argv) { */ 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 - }); + 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); + responseParams = spConfig.getResponseParams(); + debugger; + + var strategy = new SamlStrategy(responseParams, + (profile, done) => {}); + strategy.authenticate(req, params)(req, res, next); } /** @@ -569,7 +983,7 @@ function _runServer(argv) { */ app.use(function(req, res, next){ - if (argv.rollSession) { + if (argv.idpRollSession) { req.session.regenerate(function(err) { return next(); }); @@ -579,8 +993,8 @@ function _runServer(argv) { }); app.use(function(req, res, next){ - req.user = argv.config.user; - req.metadata = argv.config.metadata; + req.user = argv.idpConfig.user; + req.metadata = argv.idpConfig.metadata; req.idp = { options: idpOptions }; req.participant = getParticipant(req); next(); @@ -634,31 +1048,6 @@ function _runServer(argv) { samlp.metadata(req.idp.options)(req, res); }); - 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' - }; - - req.metadata.forEach(function(entry) { - if (entry.id === req.body.attributeName) { - entry = attribute; - attributeExists = true; - } - }); - - if (!attributeExists) { - req.metadata.push(attribute); - } - console.log("Updated SAML Attribute Metadata => \n", req.metadata) - res.status(200).end(); - } - }); app.get(IDP_PATHS.SIGN_OUT, function(req, res, next) { if (req.idp.options.sloUrl) { @@ -676,35 +1065,6 @@ function _runServer(argv) { } }); - 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'); @@ -730,15 +1090,12 @@ function _runServer(argv) { console.log('starting idp server on port %s', app.get('port')); httpServer.listen(app.get('port'), function() { - const scheme = argv.https ? 'https' : 'http', + 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: '); @@ -751,7 +1108,7 @@ function _runServer(argv) { 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) { + if (argv.idpSloUrl) { console.log('SLO Bindings: '); console.log(); console.log('\turn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); @@ -764,6 +1121,7 @@ function _runServer(argv) { console.log('\t=> ' + baseUrl); console.log(); }); +}); } function runServer(options) { 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/package-lock.json b/package-lock.json index c89a870c6..a605ee084 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": { @@ -14,18 +14,29 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "requires": { - "mime-types": "~2.1.18", + "mime-types": "2.1.18", "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.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, "amdefine": { @@ -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,21 +92,30 @@ "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.5" + } + }, "body-parser": { "version": "1.18.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", "requires": { "bytes": "3.0.0", - "content-type": "~1.0.4", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", + "depd": "1.1.2", + "http-errors": "1.6.3", "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.5.2", "raw-body": "2.3.3", - "type-is": "~1.6.16" + "type-is": "1.6.16" }, "dependencies": { "debug": { @@ -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.1" + } + }, "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,14 +158,19 @@ "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", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "optional": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "cliui": { @@ -116,8 +179,8 @@ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "optional": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -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", @@ -178,9 +259,25 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "shebang-command": "1.2.0", + "which": "1.3.1" + } + }, + "cryptiles": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", + "requires": { + "boom": "0.4.2" + } + }, + "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": { @@ -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.1" + } + }, + "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.1.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -236,13 +355,13 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "express": { @@ -250,36 +369,36 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "requires": { - "accepts": "~1.3.5", + "accepts": "1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "~1.0.4", + "content-type": "1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", "finalhandler": "1.1.1", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.3", + "proxy-addr": "2.0.3", "qs": "6.5.1", - "range-parser": "~1.2.0", + "range-parser": "1.2.0", "safe-buffer": "5.1.1", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", + "statuses": "1.4.0", + "type-is": "1.6.16", "utils-merge": "1.0.1", - "vary": "~1.1.2" + "vary": "1.1.2" }, "dependencies": { "body-parser": { @@ -288,15 +407,15 @@ "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "requires": { "bytes": "3.0.0", - "content-type": "~1.0.4", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", + "depd": "1.1.2", + "http-errors": "1.6.3", "iconv-lite": "0.4.19", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "~1.6.15" + "type-is": "1.6.16" } }, "debug": { @@ -341,7 +460,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" + "statuses": "1.4.0" } }, "setprototypeof": { @@ -367,10 +486,10 @@ "cookie-signature": "1.0.6", "crc": "3.4.4", "debug": "2.6.9", - "depd": "~1.1.1", - "on-headers": "~1.0.1", - "parseurl": "~1.3.2", - "uid-safe": "~2.1.5", + "depd": "1.1.2", + "on-headers": "1.0.1", + "parseurl": "1.3.2", + "uid-safe": "2.1.5", "utils-merge": "1.0.1" }, "dependencies": { @@ -389,18 +508,33 @@ "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", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.4.0", + "unpipe": "1.0.0" }, "dependencies": { "debug": { @@ -423,7 +557,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "flowstate": { @@ -431,16 +565,36 @@ "resolved": "https://registry.npmjs.org/flowstate/-/flowstate-0.4.1.tgz", "integrity": "sha1-tfu4t/wte9xbVL5GyYMJ73NvTsA=", "requires": { - "clone": "^1.0.2", - "uid-safe": "^2.1.0", - "utils-flatten": "^1.0.0" + "clone": "1.0.4", + "uid-safe": "2.1.5", + "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.18" + } + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -461,15 +615,37 @@ "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", "integrity": "sha1-ksbta7FkEQxQ1NjQ+93HCAbG+Oc=", "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" + } + }, + "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.5.2", + "har-schema": "2.0.0" } }, "hbs": { @@ -481,15 +657,30 @@ "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", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "requires": { - "depd": "~1.1.2", + "depd": "1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "statuses": "1.5.0" + } + }, + "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.4.1", + "sshpk": "1.14.2" } }, "iconv-lite": { @@ -497,9 +688,14 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } }, + "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,22 +726,96 @@ "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.1.5" + } + }, + "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.1.1" + } + }, + "jws": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", + "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", + "requires": { + "jwa": "1.1.6", + "safe-buffer": "5.1.1" + } + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "lazy-cache": { @@ -554,7 +829,7 @@ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "locate-path": { @@ -562,8 +837,8 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" } }, "lodash": { @@ -581,8 +856,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "media-typer": { @@ -595,7 +870,7 @@ "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "merge-descriptors": { @@ -623,7 +898,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "~1.33.0" + "mime-db": "1.33.0" } }, "mimic-fn": { @@ -646,11 +921,11 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", "requires": { - "basic-auth": "~2.0.0", + "basic-auth": "2.0.0", "debug": "2.6.9", - "depd": "~1.1.1", - "on-finished": "~2.3.0", - "on-headers": "~1.0.1" + "depd": "1.1.2", + "on-finished": "2.3.0", + "on-headers": "1.0.1" }, "dependencies": { "debug": { @@ -683,7 +958,7 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "number-is-nan": { @@ -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.5", + "indexof": "0.0.1", + "is": "0.2.7" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -709,8 +999,8 @@ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.10", + "wordwrap": "0.0.3" } }, "os-locale": { @@ -718,9 +1008,9 @@ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "p-finally": { @@ -733,7 +1023,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { @@ -741,7 +1031,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.3.0" } }, "p-try": { @@ -754,6 +1044,68 @@ "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.0.0", + "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": "3.0.11", + "resolved": "https://registry.npmjs.org/passport-wsfed-saml2/-/passport-wsfed-saml2-3.0.11.tgz", + "integrity": "sha512-a3W0x3v6hEE8/bV/bRUAXkhaHkb0gpg1KLaMTnq0T6giMgGOwTeeLYgJgkEGZPuSvFYTFDkL+tpW3DtIJQBLxw==", + "requires": { + "cryptiles": "0.2.2", + "ejs": "2.5.5", + "jsonwebtoken": "5.0.5", + "passport-strategy": "1.0.0", + "uid2": "0.0.3", + "valid-url": "1.0.9", + "xml-crypto": "github:auth0/xml-crypto#65fa1e22c5884e45135d0bacccb99ad11e900bf1", + "xml-encryption": "0.11.0", + "xml2js": "0.1.14", + "xmldom": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", + "xpath": "0.0.5", + "xtend": "2.0.6" + }, + "dependencies": { + "xml-crypto": { + "version": "github:auth0/xml-crypto#65fa1e22c5884e45135d0bacccb99ad11e900bf1", + "requires": { + "xmldom": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", + "xpath.js": "1.1.0" + } + }, + "xml2js": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.1.14.tgz", + "integrity": "sha1-UnTmf1pkxfkpdM2FE54DMq3GuQw=", + "requires": { + "sax": "1.2.4" + } + }, + "xmldom": { + "version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392" + }, + "xtend": { + "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" + } + } + } + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -769,12 +1121,22 @@ "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", "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.1.2", "ipaddr.js": "1.6.0" } }, @@ -783,6 +1145,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 +1186,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.7.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "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.18", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.1", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -835,7 +1229,7 @@ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "optional": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "safe-buffer": { @@ -848,71 +1242,73 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "saml": { - "version": "github:mcguinness/node-saml#c2a4c18861b19e604e7eed429ed567092ebc383e", - "from": "github:mcguinness/node-saml", - "requires": { - "async": "~0.2.9", - "moment": "2.15.2", - "valid-url": "~1.0.9", - "xml-crypto": "~0.10.1", - "xml-encryption": "0.11.0", - "xml-name-validator": "~2.0.1", - "xmldom": "=0.1.15", - "xpath": "0.0.5" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" - }, - "xmldom": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.15.tgz", - "integrity": "sha1-swSAYvG91S7cQhQkRZ8G3O6y+U0=" - } - } - }, "samlp": { "version": "github:mcguinness/node-samlp#e6c5e3ed4c02546e0b4008a339173a71d86b602f", - "from": "github:mcguinness/node-samlp", "requires": { "@auth0/thumbprint": "0.0.6", "ejs": "2.5.5", - "flowstate": "^0.4.0", - "querystring": "^0.2.0", + "flowstate": "0.4.1", + "querystring": "0.2.0", "saml": "github:mcguinness/node-saml#c2a4c18861b19e604e7eed429ed567092ebc383e", - "xml-crypto": "^0.10.1", + "xml-crypto": "0.10.1", "xmldom": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", "xpath": "0.0.5", - "xtend": "^1.0.3" + "xtend": "1.0.3" }, "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "saml": { + "version": "github:mcguinness/node-saml#c2a4c18861b19e604e7eed429ed567092ebc383e", + "requires": { + "async": "0.2.10", + "moment": "2.15.2", + "valid-url": "1.0.9", + "xml-crypto": "0.10.1", + "xml-encryption": "0.11.0", + "xml-name-validator": "2.0.1", + "xmldom": "0.1.15", + "xpath": "0.0.5" + }, + "dependencies": { + "xmldom": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.15.tgz", + "integrity": "sha1-swSAYvG91S7cQhQkRZ8G3O6y+U0=" + } + } + }, "xmldom": { - "version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", - "from": "github:auth0/xmldom#v0.1.19-auth0_1" + "version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392" } } }, + "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", "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", "fresh": "0.5.2", - "http-errors": "~1.6.2", + "http-errors": "1.6.3", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.4.0" }, "dependencies": { "debug": { @@ -935,9 +1331,9 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", "send": "0.16.2" } }, @@ -956,7 +1352,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -974,7 +1370,23 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" + } + }, + "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.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" } }, "statuses": { @@ -987,8 +1399,8 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -996,7 +1408,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "strip-eof": { @@ -1004,13 +1416,35 @@ "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.1.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", "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "2.1.18" } }, "uglify-js": { @@ -1019,9 +1453,9 @@ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" }, "dependencies": { "source-map": { @@ -1036,9 +1470,9 @@ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "optional": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } } @@ -1055,9 +1489,19 @@ "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", "requires": { - "random-bytes": "~1.0.0" + "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 +1517,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,12 +1532,22 @@ "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.3.0" + } + }, "walk": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.9.tgz", "integrity": "sha1-MbTbZnjyrgHDnqn7hyWpAx5Vins=", "requires": { - "foreachasync": "^3.0.0" + "foreachasync": "3.0.0" } }, "which": { @@ -1096,7 +1555,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -1120,8 +1579,8 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "ansi-regex": { @@ -1134,7 +1593,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "string-width": { @@ -1142,9 +1601,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "strip-ansi": { @@ -1152,7 +1611,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } } } @@ -1162,8 +1621,8 @@ "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-0.10.1.tgz", "integrity": "sha1-+DL3TM9W8kr8rhFjofyrRNlndKg=", "requires": { - "xmldom": "=0.1.19", - "xpath.js": ">=0.0.3" + "xmldom": "0.1.19", + "xpath.js": "1.1.0" }, "dependencies": { "xmldom": { @@ -1178,10 +1637,10 @@ "resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-0.11.0.tgz", "integrity": "sha1-RYwst9AwD/YtMEx06z3tCMqXRWs=", "requires": { - "async": "^2.1.5", - "ejs": "^2.5.6", - "node-forge": "^0.7.0", - "xmldom": "~0.1.15", + "async": "2.6.1", + "ejs": "2.6.1", + "node-forge": "0.7.5", + "xmldom": "0.1.27", "xpath": "0.0.24" }, "dependencies": { @@ -1190,7 +1649,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "requires": { - "lodash": "^4.17.10" + "lodash": "4.17.10" } }, "ejs": { @@ -1210,7 +1669,7 @@ "resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-1.0.1.tgz", "integrity": "sha1-OAgz3dC86iwJht7+u6cfhDhPNT0=", "requires": { - "xml-parser-xo": "^2.1.1" + "xml-parser-xo": "2.1.3" } }, "xml-name-validator": { @@ -1223,7 +1682,7 @@ "resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-2.1.3.tgz", "integrity": "sha1-TqjrhW36TddcSrVLJRVY3grcHGE=", "requires": { - "debug": "^2.2.0" + "debug": "2.6.9" }, "dependencies": { "debug": { @@ -1236,6 +1695,20 @@ } } }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": "1.2.4", + "xmlbuilder": "9.0.7" + } + }, + "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", @@ -1271,18 +1744,18 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" }, "dependencies": { "cliui": { @@ -1290,9 +1763,9 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" } } } @@ -1302,7 +1775,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" }, "dependencies": { "camelcase": { diff --git a/package.json b/package.json index 94c4a4b20..f6ba5e7b3 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,14 @@ "extend": "^3.0.1", "hbs": "~4.0.1", "morgan": "~1.9.0", + "passport": "^0.4.0", + "passport-wsfed-saml2": "^3.0.10", "samlp": "mcguinness/node-samlp", + "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 @@ + ForceAuthn="true"<% } %>> + @@Issuer@@ + <% if (NameIDFormat) { %><% } %> + <% if (AuthnContext) { %> + @@AuthnContext@@ + <% } %> + diff --git a/templates/metadata.tpl b/templates/metadata.tpl new file mode 100644 index 000000000..c9787ea5f --- /dev/null +++ b/templates/metadata.tpl @@ -0,0 +1,15 @@ + + + + + + + <%= cert %> + + + + + <%= nameIDFormat %> + <% for (var i=0; i<% } %> + + From 3732b09bc89314097e868b640bb9df685570a64c Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Mon, 30 Jul 2018 19:41:31 -0700 Subject: [PATCH 02/15] Add SP metadata route --- app.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app.js b/app.js index 46392e67a..d7845a617 100644 --- a/app.js +++ b/app.js @@ -1048,6 +1048,13 @@ function _runServer(argv) { samlp.metadata(req.idp.options)(req, res); }); + app.get(SP_METADATA_URL, function(req, res, next) { + const xml = METADATA_TEMPLATE(config.getMetadataParams(req)); + console.log(xml); + res.set('Content-Type', 'text/xml'); + res.send(xml); + }); + app.get(IDP_PATHS.SIGN_OUT, function(req, res, next) { if (req.idp.options.sloUrl) { From e972cf6c69ff97752211de060723ee18b708db9d Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Mon, 30 Jul 2018 19:59:52 -0700 Subject: [PATCH 03/15] config var --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index d7845a617..0cda3f288 100644 --- a/app.js +++ b/app.js @@ -1049,7 +1049,7 @@ function _runServer(argv) { }); app.get(SP_METADATA_URL, function(req, res, next) { - const xml = METADATA_TEMPLATE(config.getMetadataParams(req)); + const xml = METADATA_TEMPLATE(spConfig.getMetadataParams(req)); console.log(xml); res.set('Content-Type', 'text/xml'); res.send(xml); From 61252bb124b4d3101a7689e4ff0013c62e6d6dd5 Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Mon, 30 Jul 2018 20:00:57 -0700 Subject: [PATCH 04/15] var ref --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 0cda3f288..e55d99297 100644 --- a/app.js +++ b/app.js @@ -736,7 +736,7 @@ function _runServer(argv) { realm: this.audience, cert: removeHeaders(this.spCert), acsUrls: this.acsUrls.map(url => getReqUrl(req, url)), - sloUrl: getReqUrl(req, SLO_URL), + sloUrl: getReqUrl(req, SP_SLO_URL), nameIDFormat: this.nameIDFormat } }, From 480baf008dc19534ff6964da5f38476985db987d Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Mon, 30 Jul 2018 21:30:07 -0700 Subject: [PATCH 05/15] Put back in passport strategy --- app.js | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index e55d99297..afe10f48a 100644 --- a/app.js +++ b/app.js @@ -20,6 +20,7 @@ const express = require('express'), 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, @@ -164,6 +165,8 @@ function getPath(path) { function getReqUrl(req, path) { if (req) { + console.log('req.get: ' + req.get('x-forwarded-proto')) + console.log('req.headers[]: ' + req.headers['x-forwarded-proto']) return (req.get('x-forwarded-proto') || req.protocol) + '://' + (req.get('x-forwarded-host') || req.get('host')) + getPath(path || req.originalUrl); } }; @@ -833,6 +836,7 @@ function _runServer(argv) { app.set('view options', { layout: 'layout' }) app.engine('handlebars', hbs.__express); app.use('/samlproxy/idp/bower_components', express.static(__dirname + '/bower_components')) + app.use(passport.initialize()); // Register Helpers hbs.registerHelper('extend', function(name, context) { @@ -898,15 +902,44 @@ function _runServer(argv) { params = spConfig.getAuthnRequestParams( acsUrl, req.query.forceauthn === '' || req.query.forceAuthn === '' || req.query.forceauthn || req.query.forceAuthn, + '/samlproxy/sp/'); +/* req.authnRequest.relayState); - +*/ console.log('Generating SSO Request with Params ', params); responseParams = spConfig.getResponseParams(); debugger; var strategy = new SamlStrategy(responseParams, - (profile, done) => {}); + (profile, done) => { + console.log(); + console.log('Assertion => ' + JSON.stringify(profile, null, '\t')); + console.log(); + return done(null, { + issuer: profile.issuer, + subject: { + name: profile.nameIdAttributes.value, + format: 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); + debugger; + passport.authenticate('wsfed-saml2', params)(req, res, next); +/* strategy.authenticate(req, params)(req, res, next); +*/ } /** From c9f381126130383c54f5bb276a84ffb9afe5ade0 Mon Sep 17 00:00:00 2001 From: Edward Paget Date: Fri, 3 Aug 2018 13:36:55 -0400 Subject: [PATCH 06/15] Update package lock --- package-lock.json | 465 +++++++++++++++++++++++----------------------- 1 file changed, 235 insertions(+), 230 deletions(-) diff --git a/package-lock.json b/package-lock.json index a605ee084..382be9edd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "requires": { - "mime-types": "2.1.18", + "mime-types": "~2.1.18", "negotiator": "0.6.1" } }, @@ -23,10 +23,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" + "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": { @@ -34,9 +34,9 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" } }, "amdefine": { @@ -98,7 +98,7 @@ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "optional": true, "requires": { - "tweetnacl": "0.14.5" + "tweetnacl": "^0.14.3" } }, "body-parser": { @@ -107,15 +107,15 @@ "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", "requires": { "bytes": "3.0.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.3", + "depd": "~1.1.2", + "http-errors": "~1.6.3", "iconv-lite": "0.4.23", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.5.2", "raw-body": "2.3.3", - "type-is": "1.6.16" + "type-is": "~1.6.16" }, "dependencies": { "debug": { @@ -133,7 +133,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", "requires": { - "hoek": "0.9.1" + "hoek": "0.9.x" } }, "bower": { @@ -169,8 +169,8 @@ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "optional": true, "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" } }, "cliui": { @@ -179,8 +179,8 @@ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "optional": true, "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", + "center-align": "^0.1.1", + "right-align": "^0.1.1", "wordwrap": "0.0.2" }, "dependencies": { @@ -212,7 +212,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "content-disposition": { @@ -259,9 +259,9 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "requires": { - "lru-cache": "4.1.3", - "shebang-command": "1.2.0", - "which": "1.3.1" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, "cryptiles": { @@ -269,7 +269,7 @@ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", "requires": { - "boom": "0.4.2" + "boom": "0.4.x" } }, "dashdash": { @@ -277,7 +277,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "debug": { @@ -314,7 +314,7 @@ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "ecdsa-sig-formatter": { @@ -322,7 +322,7 @@ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "ee-first": { @@ -355,13 +355,13 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" } }, "express": { @@ -369,36 +369,36 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "requires": { - "accepts": "1.3.5", + "accepts": "~1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "1.0.4", + "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "finalhandler": "1.1.1", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.3", + "proxy-addr": "~2.0.3", "qs": "6.5.1", - "range-parser": "1.2.0", + "range-parser": "~1.2.0", "safe-buffer": "5.1.1", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", - "statuses": "1.4.0", - "type-is": "1.6.16", + "statuses": "~1.4.0", + "type-is": "~1.6.16", "utils-merge": "1.0.1", - "vary": "1.1.2" + "vary": "~1.1.2" }, "dependencies": { "body-parser": { @@ -407,15 +407,15 @@ "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "requires": { "bytes": "3.0.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.3", + "depd": "~1.1.1", + "http-errors": "~1.6.2", "iconv-lite": "0.4.19", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "1.6.16" + "type-is": "~1.6.15" } }, "debug": { @@ -460,7 +460,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": "1.4.0" + "statuses": ">= 1.3.1 < 2" } }, "setprototypeof": { @@ -486,10 +486,10 @@ "cookie-signature": "1.0.6", "crc": "3.4.4", "debug": "2.6.9", - "depd": "1.1.2", - "on-headers": "1.0.1", - "parseurl": "1.3.2", - "uid-safe": "2.1.5", + "depd": "~1.1.1", + "on-headers": "~1.0.1", + "parseurl": "~1.3.2", + "uid-safe": "~2.1.5", "utils-merge": "1.0.1" }, "dependencies": { @@ -529,12 +529,12 @@ "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "requires": { "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.4.0", - "unpipe": "1.0.0" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" }, "dependencies": { "debug": { @@ -557,7 +557,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "flowstate": { @@ -565,9 +565,9 @@ "resolved": "https://registry.npmjs.org/flowstate/-/flowstate-0.4.1.tgz", "integrity": "sha1-tfu4t/wte9xbVL5GyYMJ73NvTsA=", "requires": { - "clone": "1.0.4", - "uid-safe": "2.1.5", - "utils-flatten": "1.0.0" + "clone": "^1.0.2", + "uid-safe": "^2.1.0", + "utils-flatten": "^1.0.0" } }, "foreach": { @@ -590,9 +590,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { - "asynckit": "0.4.0", + "asynckit": "^0.4.0", "combined-stream": "1.0.6", - "mime-types": "2.1.18" + "mime-types": "^2.1.12" } }, "forwarded": { @@ -620,7 +620,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "handlebars": { @@ -628,10 +628,10 @@ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.5.tgz", "integrity": "sha1-ksbta7FkEQxQ1NjQ+93HCAbG+Oc=", "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" } }, "har-schema": { @@ -644,8 +644,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" + "ajv": "^5.1.0", + "har-schema": "^2.0.0" } }, "hbs": { @@ -667,10 +667,10 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "requires": { - "depd": "1.1.2", + "depd": "~1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": "1.5.0" + "statuses": ">= 1.4.0 < 2" } }, "http-signature": { @@ -678,9 +678,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.14.2" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "iconv-lite": { @@ -688,7 +688,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } }, "indexof": { @@ -777,7 +777,7 @@ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-5.0.5.tgz", "integrity": "sha1-ZZLMBe4D3VrZ4DqRCRGk2nmv4Pg=", "requires": { - "jws": "3.1.5" + "jws": "^3.0.0" } }, "jsprim": { @@ -798,7 +798,7 @@ "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.10", - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "jws": { @@ -806,8 +806,8 @@ "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", "requires": { - "jwa": "1.1.6", - "safe-buffer": "5.1.1" + "jwa": "^1.1.5", + "safe-buffer": "^5.0.1" } }, "kind-of": { @@ -815,7 +815,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } }, "lazy-cache": { @@ -829,7 +829,7 @@ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { - "invert-kv": "1.0.0" + "invert-kv": "^1.0.0" } }, "locate-path": { @@ -837,8 +837,8 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" } }, "lodash": { @@ -856,8 +856,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "media-typer": { @@ -870,7 +870,7 @@ "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "requires": { - "mimic-fn": "1.2.0" + "mimic-fn": "^1.0.0" } }, "merge-descriptors": { @@ -898,7 +898,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } }, "mimic-fn": { @@ -921,11 +921,11 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", "requires": { - "basic-auth": "2.0.0", + "basic-auth": "~2.0.0", "debug": "2.6.9", - "depd": "1.1.2", - "on-finished": "2.3.0", - "on-headers": "1.0.1" + "depd": "~1.1.1", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" }, "dependencies": { "debug": { @@ -958,7 +958,7 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "requires": { - "path-key": "2.0.1" + "path-key": "^2.0.0" } }, "number-is-nan": { @@ -976,9 +976,9 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.2.0.tgz", "integrity": "sha1-zd7AKZiwkb5CvxA1rjLknxy26mc=", "requires": { - "foreach": "2.0.5", - "indexof": "0.0.1", - "is": "0.2.7" + "foreach": "~2.0.1", + "indexof": "~0.0.1", + "is": "~0.2.6" } }, "on-finished": { @@ -999,8 +999,8 @@ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "requires": { - "minimist": "0.0.10", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" } }, "os-locale": { @@ -1008,9 +1008,9 @@ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" } }, "p-finally": { @@ -1023,7 +1023,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "requires": { - "p-try": "1.0.0" + "p-try": "^1.0.0" } }, "p-locate": { @@ -1031,7 +1031,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "requires": { - "p-limit": "1.3.0" + "p-limit": "^1.1.0" } }, "p-try": { @@ -1049,7 +1049,7 @@ "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", "requires": { - "passport-strategy": "1.0.0", + "passport-strategy": "1.x.x", "pause": "0.0.1" } }, @@ -1063,25 +1063,26 @@ "resolved": "https://registry.npmjs.org/passport-wsfed-saml2/-/passport-wsfed-saml2-3.0.11.tgz", "integrity": "sha512-a3W0x3v6hEE8/bV/bRUAXkhaHkb0gpg1KLaMTnq0T6giMgGOwTeeLYgJgkEGZPuSvFYTFDkL+tpW3DtIJQBLxw==", "requires": { - "cryptiles": "0.2.2", + "cryptiles": "~0.2.2", "ejs": "2.5.5", - "jsonwebtoken": "5.0.5", - "passport-strategy": "1.0.0", - "uid2": "0.0.3", - "valid-url": "1.0.9", + "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.14", + "xml2js": "0.1.x", "xmldom": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", "xpath": "0.0.5", - "xtend": "2.0.6" + "xtend": "~2.0.3" }, "dependencies": { "xml-crypto": { "version": "github:auth0/xml-crypto#65fa1e22c5884e45135d0bacccb99ad11e900bf1", + "from": "xml-crypto@github:auth0/xml-crypto#65fa1e22c5884e45135d0bacccb99ad11e900bf1", "requires": { - "xmldom": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", - "xpath.js": "1.1.0" + "xmldom": "=0.1.19", + "xpath.js": ">=0.0.3" } }, "xml2js": { @@ -1089,19 +1090,20 @@ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.1.14.tgz", "integrity": "sha1-UnTmf1pkxfkpdM2FE54DMq3GuQw=", "requires": { - "sax": "1.2.4" + "sax": ">=0.1.1" } }, "xmldom": { - "version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392" + "version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", + "from": "xmldom@github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392" }, "xtend": { "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" + "is-object": "~0.1.2", + "object-keys": "~0.2.0" } } } @@ -1136,7 +1138,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", "requires": { - "forwarded": "0.1.2", + "forwarded": "~0.1.2", "ipaddr.js": "1.6.0" } }, @@ -1191,26 +1193,26 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.7.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "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.18", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.2", - "safe-buffer": "5.1.1", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.6.0", - "uuid": "3.3.2" + "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": { @@ -1229,7 +1231,7 @@ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "optional": true, "requires": { - "align-text": "0.1.4" + "align-text": "^0.1.1" } }, "safe-buffer": { @@ -1244,16 +1246,17 @@ }, "samlp": { "version": "github:mcguinness/node-samlp#e6c5e3ed4c02546e0b4008a339173a71d86b602f", + "from": "github:mcguinness/node-samlp", "requires": { "@auth0/thumbprint": "0.0.6", "ejs": "2.5.5", - "flowstate": "0.4.1", - "querystring": "0.2.0", + "flowstate": "^0.4.0", + "querystring": "^0.2.0", "saml": "github:mcguinness/node-saml#c2a4c18861b19e604e7eed429ed567092ebc383e", - "xml-crypto": "0.10.1", + "xml-crypto": "^0.10.1", "xmldom": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", "xpath": "0.0.5", - "xtend": "1.0.3" + "xtend": "^1.0.3" }, "dependencies": { "async": { @@ -1263,14 +1266,15 @@ }, "saml": { "version": "github:mcguinness/node-saml#c2a4c18861b19e604e7eed429ed567092ebc383e", + "from": "github:mcguinness/node-saml#c2a4c18861b19e604e7eed429ed567092ebc383e", "requires": { - "async": "0.2.10", + "async": "~0.2.9", "moment": "2.15.2", - "valid-url": "1.0.9", - "xml-crypto": "0.10.1", + "valid-url": "~1.0.9", + "xml-crypto": "~0.10.1", "xml-encryption": "0.11.0", - "xml-name-validator": "2.0.1", - "xmldom": "0.1.15", + "xml-name-validator": "~2.0.1", + "xmldom": "=0.1.15", "xpath": "0.0.5" }, "dependencies": { @@ -1282,7 +1286,8 @@ } }, "xmldom": { - "version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392" + "version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", + "from": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392" } } }, @@ -1297,18 +1302,18 @@ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", "requires": { "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.6.3", + "http-errors": "~1.6.2", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.4.0" + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" }, "dependencies": { "debug": { @@ -1331,9 +1336,9 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "requires": { - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "parseurl": "1.3.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", "send": "0.16.2" } }, @@ -1352,7 +1357,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "requires": { - "shebang-regex": "1.0.0" + "shebang-regex": "^1.0.0" } }, "shebang-regex": { @@ -1370,7 +1375,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } }, "sshpk": { @@ -1378,15 +1383,15 @@ "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.2", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "safer-buffer": "2.1.2", - "tweetnacl": "0.14.5" + "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": { @@ -1399,8 +1404,8 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -1408,7 +1413,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "strip-eof": { @@ -1421,7 +1426,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "requires": { - "punycode": "1.4.1" + "punycode": "^1.4.1" } }, "tunnel-agent": { @@ -1429,7 +1434,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { @@ -1444,7 +1449,7 @@ "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.18" + "mime-types": "~2.1.18" } }, "uglify-js": { @@ -1453,9 +1458,9 @@ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "optional": true, "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" }, "dependencies": { "source-map": { @@ -1470,9 +1475,9 @@ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "optional": true, "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", "window-size": "0.1.0" } } @@ -1489,7 +1494,7 @@ "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", "requires": { - "random-bytes": "1.0.0" + "random-bytes": "~1.0.0" } }, "uid2": { @@ -1537,9 +1542,9 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "1.0.0", + "assert-plus": "^1.0.0", "core-util-is": "1.0.2", - "extsprintf": "1.3.0" + "extsprintf": "^1.2.0" } }, "walk": { @@ -1547,7 +1552,7 @@ "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.9.tgz", "integrity": "sha1-MbTbZnjyrgHDnqn7hyWpAx5Vins=", "requires": { - "foreachasync": "3.0.0" + "foreachasync": "^3.0.0" } }, "which": { @@ -1555,7 +1560,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "which-module": { @@ -1579,8 +1584,8 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" }, "dependencies": { "ansi-regex": { @@ -1593,7 +1598,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "string-width": { @@ -1601,9 +1606,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "strip-ansi": { @@ -1611,7 +1616,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } } } @@ -1621,8 +1626,8 @@ "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-0.10.1.tgz", "integrity": "sha1-+DL3TM9W8kr8rhFjofyrRNlndKg=", "requires": { - "xmldom": "0.1.19", - "xpath.js": "1.1.0" + "xmldom": "=0.1.19", + "xpath.js": ">=0.0.3" }, "dependencies": { "xmldom": { @@ -1637,10 +1642,10 @@ "resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-0.11.0.tgz", "integrity": "sha1-RYwst9AwD/YtMEx06z3tCMqXRWs=", "requires": { - "async": "2.6.1", - "ejs": "2.6.1", - "node-forge": "0.7.5", - "xmldom": "0.1.27", + "async": "^2.1.5", + "ejs": "^2.5.6", + "node-forge": "^0.7.0", + "xmldom": "~0.1.15", "xpath": "0.0.24" }, "dependencies": { @@ -1649,7 +1654,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "requires": { - "lodash": "4.17.10" + "lodash": "^4.17.10" } }, "ejs": { @@ -1669,7 +1674,7 @@ "resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-1.0.1.tgz", "integrity": "sha1-OAgz3dC86iwJht7+u6cfhDhPNT0=", "requires": { - "xml-parser-xo": "2.1.3" + "xml-parser-xo": "^2.1.1" } }, "xml-name-validator": { @@ -1682,7 +1687,7 @@ "resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-2.1.3.tgz", "integrity": "sha1-TqjrhW36TddcSrVLJRVY3grcHGE=", "requires": { - "debug": "2.6.9" + "debug": "^2.2.0" }, "dependencies": { "debug": { @@ -1700,8 +1705,8 @@ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", "requires": { - "sax": "1.2.4", - "xmlbuilder": "9.0.7" + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" } }, "xmlbuilder": { @@ -1744,18 +1749,18 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", "requires": { - "cliui": "4.1.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "9.0.2" + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" }, "dependencies": { "cliui": { @@ -1763,9 +1768,9 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" } } } @@ -1775,7 +1780,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" }, "dependencies": { "camelcase": { From fead18cec6ced10e6bd80770ba805437dd598eae Mon Sep 17 00:00:00 2001 From: Edward Paget Date: Tue, 7 Aug 2018 19:30:16 -0400 Subject: [PATCH 07/15] Update to hacked libraries and saml code --- app.js | 1176 ++++++++++++++++++------------------ config.js | 68 ++- lib/simpleProfileMapper.js | 7 +- package-lock.json | 102 ++-- package.json | 4 +- 5 files changed, 695 insertions(+), 662 deletions(-) diff --git a/app.js b/app.js index afe10f48a..4927afb1f 100644 --- a/app.js +++ b/app.js @@ -58,7 +58,7 @@ 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-Redirect' + POST: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' } const NAMEID_FORMAT_PREFERENCE = [ @@ -72,11 +72,11 @@ const NAMEID_FORMAT_PREFERENCE = [ 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"; @@ -130,7 +130,7 @@ 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 : '') ) }; } @@ -165,8 +165,6 @@ function getPath(path) { function getReqUrl(req, path) { if (req) { - console.log('req.get: ' + req.get('x-forwarded-proto')) - console.log('req.headers[]: ' + req.headers['x-forwarded-proto']) return (req.get('x-forwarded-proto') || req.protocol) + '://' + (req.get('x-forwarded-host') || req.get('host')) + getPath(path || req.originalUrl); } }; @@ -197,8 +195,8 @@ 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({ idpPort: { description: 'IdP Web Server Listener Port', @@ -263,7 +261,7 @@ function processArgs(args, options) { }, 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, coerce: makeCertFileCoercer('public key', 'Encryption public key') @@ -545,623 +543,653 @@ function _runServer(argv) { } switch (metadata.protocol) { - case 'samlp': - if (metadata.sso.redirectUrl) { - argv.spIdpSsoUrl = metadata.sso.redirectUrl; - argv.spIdpSsoBinding = BINDINGS.REDIRECT - } else if (metadata.sso.postUrl) { - argv.spIdpSsoUrl = metadata.sso.postUrl; - argv.spIdpSsoBinding = BINDINGS.POST - } - if (metadata.slo.redirectUrl) { - argv.spIdpSloUrl = metadata.slo.redirectUrl; - argv.spIdpSloBinding = BINDINGS.REDIRECT - } else if (metadata.slo.postUrl) { - argv.spIdpSloUrl = metadata.slo.postUrl; - argv.spIdpSloBinding = BINDINGS.POST - } - if (metadata.signRequest) { - argv.spSignAuthnRequests = metadata.signRequest; - } - break; - case 'wsfed': - if (metadata.sso.redirectUrl) { - argv.spIdpSsoUrl = metadata.sso.redirectUrl; - } + 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 = {}; + 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('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(); + 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('[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(); + console.log(); - /** - * 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(`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(); - 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 + /** + * 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(`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 + }); + } } - }, - 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 + 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 + } }, - 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 - } - if (this.signAuthnRequests) { - params.signingKey = { - cert: this.spCert, - key: this.spKey + 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 + } + + 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 + } } - } - 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 - } - } - }; - - - /** - * 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('/samlproxy/idp/bower_components', express.static(__dirname + '/bower_components')) - app.use(passport.initialize()); - - // 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 + */ - hbs.registerHelper('select', function(selected, options) { - return options.fn(this).replace( - new RegExp(' value=\"' + selected + '\"'), '$& selected="selected"'); - }); + app.set('port', process.env.PORT || argv.idpPort); + app.set('views', path.join(__dirname, 'views')); - hbs.registerHelper('getProperty', function(attribute, context) { - return context[attribute]; - }); + /** + * View Engine + */ - hbs.registerHelper('serialize', function(context) { - return new Buffer(JSON.stringify(context)).toString('base64'); - }); + 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')) + app.use(passport.initialize()); - /** - * Middleware - */ + // Register Helpers + hbs.registerHelper('extend', function(name, context) { + var block = blocks[name]; + if (!block) { + block = blocks[name] = []; + } - 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) { - 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, - '/samlproxy/sp/'); -/* - req.authnRequest.relayState); -*/ - console.log('Generating SSO Request with Params ', params); - responseParams = spConfig.getResponseParams(); - debugger; - - var strategy = new SamlStrategy(responseParams, - (profile, done) => { - console.log(); - console.log('Assertion => ' + JSON.stringify(profile, null, '\t')); - console.log(); - return done(null, { - issuer: profile.issuer, - subject: { - name: profile.nameIdAttributes.value, - format: 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() - }); + block.push(context.fn(this)); + }); - } - ); - passport.use(strategy); - debugger; - passport.authenticate('wsfed-saml2', params)(req, res, next); -/* - strategy.authenticate(req, params)(req, res, next); -*/ - } + hbs.registerHelper('block', function(name) { + const val = (blocks[name] || []).join('\n'); + // clear the block + blocks[name] = []; + return val; + }); + + + hbs.registerHelper('select', function(selected, options) { + return options.fn(this).replace( + new RegExp(' value=\"' + selected + '\"'), '$& selected="selected"'); + }); - /** - * Shared Handlers - */ + hbs.registerHelper('getProperty', function(attribute, context) { + return context[attribute]; + }); + + hbs.registerHelper('serialize', function(context) { + return new Buffer(JSON.stringify(context)).toString('base64'); + }); - 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 + /** + * 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(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) { + 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, + '/samlproxy/sp/' + ); + console.log('Generating SSO Request with Params ', params); + passport.authenticate('wsfed-saml2', params)(req, res, next); + }; + + /** + * 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.idpRollSession) { - 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.idpConfig.user; - req.metadata = argv.idpConfig.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.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); + }); + 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); + if (req.session && req.session.authnRequest) { + authOptions.RelayState = decodeURIComponent(req.session.authnRequest.relayState); + } + 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.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; + 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'); + }) } - res.redirect('back'); - }) - } - }); + }); - // 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); - }); + // 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 + // 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.idpHttps ? '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; - 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(); - }); -}); + 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/config.js b/config.js index f0247a5ec..e894d5e39 100644 --- a/config.js +++ b/config.js @@ -3,61 +3,75 @@ * 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 }]; module.exports = { 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 382be9edd..7d860fed9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1059,9 +1059,8 @@ "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" }, "passport-wsfed-saml2": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/passport-wsfed-saml2/-/passport-wsfed-saml2-3.0.11.tgz", - "integrity": "sha512-a3W0x3v6hEE8/bV/bRUAXkhaHkb0gpg1KLaMTnq0T6giMgGOwTeeLYgJgkEGZPuSvFYTFDkL+tpW3DtIJQBLxw==", + "version": "github:edpaget/passport-wsfed-saml2#2667ba83974af3fc1dba75ed3a62faaf04e8aa06", + "from": "github:edpaget/passport-wsfed-saml2#auth-demo", "requires": { "cryptiles": "~0.2.2", "ejs": "2.5.5", @@ -1077,14 +1076,6 @@ "xtend": "~2.0.3" }, "dependencies": { - "xml-crypto": { - "version": "github:auth0/xml-crypto#65fa1e22c5884e45135d0bacccb99ad11e900bf1", - "from": "xml-crypto@github:auth0/xml-crypto#65fa1e22c5884e45135d0bacccb99ad11e900bf1", - "requires": { - "xmldom": "=0.1.19", - "xpath.js": ">=0.0.3" - } - }, "xml2js": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.1.14.tgz", @@ -1095,16 +1086,7 @@ }, "xmldom": { "version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", - "from": "xmldom@github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392" - }, - "xtend": { - "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" - } + "from": "github:auth0/xmldom#v0.1.19-auth0_1" } } }, @@ -1244,9 +1226,35 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "saml": { + "version": "github:mcguinness/node-saml#c2a4c18861b19e604e7eed429ed567092ebc383e", + "from": "github:mcguinness/node-saml", + "requires": { + "async": "~0.2.9", + "moment": "2.15.2", + "valid-url": "~1.0.9", + "xml-crypto": "~0.10.1", + "xml-encryption": "0.11.0", + "xml-name-validator": "~2.0.1", + "xmldom": "=0.1.15", + "xpath": "0.0.5" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "xmldom": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.15.tgz", + "integrity": "sha1-swSAYvG91S7cQhQkRZ8G3O6y+U0=" + } + } + }, "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", @@ -1259,35 +1267,14 @@ "xtend": "^1.0.3" }, "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" - }, - "saml": { - "version": "github:mcguinness/node-saml#c2a4c18861b19e604e7eed429ed567092ebc383e", - "from": "github:mcguinness/node-saml#c2a4c18861b19e604e7eed429ed567092ebc383e", - "requires": { - "async": "~0.2.9", - "moment": "2.15.2", - "valid-url": "~1.0.9", - "xml-crypto": "~0.10.1", - "xml-encryption": "0.11.0", - "xml-name-validator": "~2.0.1", - "xmldom": "=0.1.15", - "xpath": "0.0.5" - }, - "dependencies": { - "xmldom": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.15.tgz", - "integrity": "sha1-swSAYvG91S7cQhQkRZ8G3O6y+U0=" - } - } - }, "xmldom": { "version": "github:auth0/xmldom#3376bc7beb5551bf68e12b0cc6b0e3669f77d392", - "from": "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=" } } }, @@ -1622,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" @@ -1730,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 f6ba5e7b3..09f0e6183 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,8 @@ "hbs": "~4.0.1", "morgan": "~1.9.0", "passport": "^0.4.0", - "passport-wsfed-saml2": "^3.0.10", - "samlp": "mcguinness/node-samlp", + "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", From 86d3b5e90e7e8f2d415b851318711c6e85c4d27b Mon Sep 17 00:00:00 2001 From: Edward Paget Date: Wed, 8 Aug 2018 14:49:37 -0400 Subject: [PATCH 08/15] Map birth date --- config.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config.js b/config.js index e894d5e39..751db5c81 100644 --- a/config.js +++ b/config.js @@ -72,6 +72,12 @@ var metadata = [{ 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 = { From 40a4bd0b8e77046dd393ffba4a643c8b71dfc049 Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Wed, 15 Aug 2018 16:20:37 -0700 Subject: [PATCH 09/15] Relay state in request, response via session --- app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 4927afb1f..0b52d1bf4 100644 --- a/app.js +++ b/app.js @@ -928,7 +928,7 @@ function _runServer(argv) { params = spConfig.getAuthnRequestParams( acsUrl, req.query.forceauthn === '' || req.query.forceAuthn === '' || req.query.forceauthn || req.query.forceAuthn, - '/samlproxy/sp/' + req.authnRequest.relayState ); console.log('Generating SSO Request with Params ', params); passport.authenticate('wsfed-saml2', params)(req, res, next); @@ -1109,7 +1109,7 @@ function _runServer(argv) { function(req, res, next) { const authOptions = extend({}, req.idp.options); if (req.session && req.session.authnRequest) { - authOptions.RelayState = decodeURIComponent(req.session.authnRequest.relayState); + authOptions.RelayState = req.session.authnRequest.relayState; } console.log('Sending SAML Response\nUser => \n%s\nOptions => \n', JSON.stringify(req.user, null, 2), authOptions); From 9f885ea1a9814af1ac0afb032f0505b4d327db97 Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Wed, 15 Aug 2018 20:22:52 -0700 Subject: [PATCH 10/15] Pass RelayState directly through response flow --- app.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 0b52d1bf4..3465942fc 100644 --- a/app.js +++ b/app.js @@ -679,6 +679,7 @@ function _runServer(argv) { }, 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(); @@ -1108,10 +1109,8 @@ function _runServer(argv) { }, function(req, res, next) { const authOptions = extend({}, req.idp.options); - if (req.session && req.session.authnRequest) { - authOptions.RelayState = req.session.authnRequest.relayState; - } - console.log('Sending SAML Response\nUser => \n%s\nOptions => \n', + 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); }); From 73ad9d0d1eab733df5f593d43f6727442d805972 Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Thu, 16 Aug 2018 09:05:22 -0700 Subject: [PATCH 11/15] Remove navbar, make proxy headless --- views/layout.hbs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/views/layout.hbs b/views/layout.hbs index d7af4e369..34b6e5c52 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -14,24 +14,6 @@ - - - -
{{{body}}} From 0c322a406179d46e75104924452dda1571dd14be Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Thu, 16 Aug 2018 10:28:43 -0700 Subject: [PATCH 12/15] Fix laoding of static bower assets --- app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 3465942fc..f02c28cde 100644 --- a/app.js +++ b/app.js @@ -862,7 +862,8 @@ function _runServer(argv) { 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')) + 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 @@ -908,7 +909,6 @@ function _runServer(argv) { })); 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, From ca0937580125cab655c22355b4e40e53815779c5 Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Thu, 16 Aug 2018 10:30:13 -0700 Subject: [PATCH 13/15] Streamline - don't need jquery/boostrap/css --- views/layout.hbs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/views/layout.hbs b/views/layout.hbs index 34b6e5c52..a84572491 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -5,9 +5,11 @@ Simple Identity Provider + + {{{block "scripts"}}} From cdbdbed1caf3cde8d8c867012d925df5735ccb17 Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Thu, 16 Aug 2018 11:09:31 -0700 Subject: [PATCH 14/15] Fix up dockerfiles --- Dockerfile | 6 +++++- docker-compose.yml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) 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/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" From fab66f64b6a31555537304fb0cb50aa0383b8ec7 Mon Sep 17 00:00:00 2001 From: Patrick Vinograd Date: Thu, 16 Aug 2018 11:13:19 -0700 Subject: [PATCH 15/15] Cleanup package metadata --- bower.json | 6 +++--- package.json | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) 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/package.json b/package.json index 09f0e6183..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": {