From 65b97781b918bf084eba0cb2058b5a6abe7e4d02 Mon Sep 17 00:00:00 2001 From: Jeyaraman Soundararajan Date: Wed, 28 Sep 2022 09:44:31 -0400 Subject: [PATCH] Revert 2 (#10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * initial commit * Initial commit * Add security notice to readme * Added logic to error if the options are missing the identityProviderUrl login endpoint * Updated the error message and added unit test cases * Added pattern match for expected generated URL * Switching from block-scoped declaration to var to fix TravisCI error * 3.0.6 * Removing the moment package dependency and removing all imports of the package * 3.0.7 * Fix issue with digest with extra whitespaces (#70) * Added digest issue test * bumped library * remove github tag * Changed tag * Removing the package-lock.json file. This serves no purpose and should not be published per https://docs.npmjs.com/files/package-lock.json * Improve error message when signing certificate is invalid (#72) * Improve error message when the provided signing certificate is invalid * Convert indentation to spaces * Added tests for the encoded XML value in an AttributeValue field (#74) * Added tests for the encoded XML value in an AttributeValue field * Updated the xmldom dependency to point to a release tag. * 3.0.8 (#73) * Fix remaining cases where an invalid identityProviderUrl isn't handled (#77) We used to parse `options.identityProviderUrl` assuming it was a string, which failed when it wasn't. We'd fixed this in Samlp.getSamlRequestUrl(), but its siblings getSamlRequestParams() and getSamlRequestForm() were still susceptible. * 3.0.9 * fix signature location lookup * Update the security notice file with a new entry. * 3.0.10 (#80) * Handle exception when signing a malformed requestTemplate (#88) 3.0.11 Handle exception when signing a malformed requestTemplate * DF-38 Wrapping template parse to prevent uncaught (#90) ## ✏️ Changes There is a situation in which the @@ VAR @@ can get goofed up and contain strings that will include part of the template itself. This will not parse correctly and throws an exception in the replace phase of the regex matching. I've wrapped it in a try/catch to prevent it from bringing the process down. Noting that this area looks problematic as it was recently patched in https://github.com/auth0/passport-wsfed-saml2/pull/88 Version bumped to `3.0.12`. ## 🔗 References [Sentry Issue](https://sentry.io/auth0/auth0-server/issues/617753396/events/25564560312/) ## 🎯 Testing ✅ This change has unit test coverage * Isolating handling of malformed template (#93) The `supplant` function will explode when a template is passed in this manner, so this will return a corresponding error message when handing the try/catch in this way. If we add proper XML validation down the line, we can widen this out. * Include option to check SAML certificate expiration (#96) * add certificate expiration checking as an option (off by default) Drops Node4 support as well, though it was already failing on Node4 in master changes per machuga comments changes per machuga comments remove separate supported-versions list for saml * restore test we lost in bad squash * change some vars to lets and camel to snake case in tests * add test to ensure we can allow expired certs if we choose to * remove unneeded comment from tests * Fix documentation for ADFS strategy configuration. (#95) * Tighten clock skew checks for SAML. (#97) Changing default clock skew to 3 minutes to adhere to standard industry practice. Also make clock skew a configurable option. * Use crypto randombytes for uniqueness (#104) * Use crypto randombytes for uniqueness * Conform id length with the spec * Added a variant of XSW test (#107) Adding a variant of xml signature wrapping unit tests in light of recent samlify vulnerability. * Add support of new error message format (for OpenSSL errors) of Node.js (#98) fixes https://github.com/auth0/passport-wsfed-saml2/issues/75 @machuga Bugfix for issue `Invalid cert tests break with Node >= 8.7.0 #75` * Add event for certificate expiration validation (#114) * Add event for certificate expiration validation * emit always * fix * fix cert validation * 3.0.14 (#115) * Fix x509 dependency (#116) * Fix x509 dependency * 3.0.15 * change dependency * fix cert validation (#118) * Catch x509 exeptions on invalid certificates (#124) * 3.0.17 (#125) * [Node 10] Update x509 (#130) * [Node 10] Update x509 * 4.0.0 * Update travis * get rid of npm install * Fix securty deps. (#137) * remove cryptiles * bump xml-encryption * express upgrade * bump request version (#138) * Bump XML Crypto version (#139) * use last xml-crypto * Change x509 dependency (#142) * 4.2.0 * Bump xml-encryption to 1.2.1 for modern algorithm support (#145) * new version: 4.3.0 (#146) * Move CI away from Travis and to Github Actions (#147) * Forgotten workflows folder (#148) * Run tests on all branch pushes and PRs (#149) * Use @auth0/xmldom (#150) Use custom npm lib with custom versions of xmldom Tag v0.1.19-auth0.2 is now version v0.1.22 * Bump to 4.4.0 (#151) * add Destination & AssertionConsumerServiceURL as SAML req template vars * 4.5.1 (#155) * Drop node 10, update/consolidate dev deps (#157) * Handle encoded CR entities in assertions (#158) * Allow options.assertionConsumerServiceURL or options.callback (#156) * Revert "Handle encoded CR entities in assertions (#158)" (#159) * chore(ci): configure Semantic Release publishing * chore: use npm i instead of npm ci * fix(pkg): upgrade xml-crypto to latest version Addresses: ``` npm WARN deprecated xmldom@0.1.27: Deprecated due to CVE-2021-21366 resolved in 0.5.0 ``` Co-authored-by: woloski Co-authored-by: German Lena Co-authored-by: Sandrino Di Mattia Co-authored-by: Mike Lee Co-authored-by: Mike Lee <33419919+mikeops@users.noreply.github.com> Co-authored-by: Marcos Castany Co-authored-by: Gustavo Narea Co-authored-by: Eduardo Diaz Co-authored-by: radekk Co-authored-by: Eduardo Diaz Co-authored-by: Matthew Machuga Co-authored-by: Robert Co-authored-by: Arkadiusz Kaɫkus Co-authored-by: gkwang Co-authored-by: Eva Sarafianou Co-authored-by: Fady Makram Co-authored-by: Jose Luis Diaz Co-authored-by: Yamil Asusta Co-authored-by: Robin Bijlani Co-authored-by: Hernan Zalazar Co-authored-by: Nico Sabena Co-authored-by: KalleV --- .gitignore | 6 + .jshintrc | 39 + .npmignore | 13 + .npmrc | 1 + LICENSE | 21 + README.md | 133 +++ SECURITY-NOTICE.md | 47 ++ examples/auth0/app.js | 90 ++ examples/auth0/package.json | 10 + examples/auth0/views/account.ejs | 2 + examples/auth0/views/index.ejs | 5 + examples/auth0/views/layout.ejs | 21 + examples/login/app.js | 144 ++++ examples/login/package.json | 14 + examples/login/views/account.ejs | 2 + examples/login/views/index.ejs | 5 + examples/login/views/layout.ejs | 21 + .../errors/AuthenticationFailedError.js | 13 + .../errors/SamlAssertionParserError.js | 13 + .../errors/SamlResponseParserError.js | 13 + .../errors/WSFederationResultParserError.js | 13 + lib/passport-wsfed-saml2/index.js | 8 + lib/passport-wsfed-saml2/saml.js | 429 ++++++++++ lib/passport-wsfed-saml2/samlp.js | 503 +++++++++++ lib/passport-wsfed-saml2/state/null.js | 12 + lib/passport-wsfed-saml2/state/session.js | 85 ++ lib/passport-wsfed-saml2/strategy.js | 300 +++++++ lib/passport-wsfed-saml2/templates.js | 11 + .../templates/assert_and_destination.ejs | 6 + lib/passport-wsfed-saml2/templates/form.ejs | 21 + .../templates/samlrequest.ejs | 7 + lib/passport-wsfed-saml2/utils.js | 113 +++ lib/passport-wsfed-saml2/wsfederation.js | 114 +++ package.json | 76 ++ test/fixture/samlp-server.js | 432 ++++++++++ test/fixture/wsfed-server.js | 106 +++ test/helpers.js | 65 ++ test/interop.tests.js | 500 +++++++++++ test/jwt.tests.js | 45 + test/saml11.tests.js | 127 +++ test/saml20.tests.js | 294 +++++++ test/samlp.functional.tests.js | 664 +++++++++++++++ test/samlp.tests.js | 792 ++++++++++++++++++ .../samlrequest_signed_differentcert.txt | 1 + .../encoded/samlresponse_encoded_xml.txt | 1 + .../samlresponse_encrypted_and_signed.txt | 1 + .../encoded/samlresponse_extraspace.txt | 1 + .../samlresponse_signedassertion_dsprefix.txt | 1 + test/samples/encoded/samlresponse_utf8.txt | 1 + .../plain/samlresponse_explicit_iso.txt | 33 + test/samples/plain/samlresponse_iso.txt | 32 + .../samlresponse_saml11_invalid_cert.txt | 1 + .../samlresponse_saml20_invalid_cert.txt | 1 + test/soap-fault-no-info.xml | 5 + test/soap-fault.xml | 16 + test/state/samlp.state.custom.tests.js | 363 ++++++++ test/state/samlp.state.session.tests.js | 441 ++++++++++ test/state/wsfed.state.custom.tests.js | 371 ++++++++ test/state/wsfed.state.session.tests.js | 446 ++++++++++ test/test-auth0-2.cer | 1 + test/test-auth0.cer | 1 + test/test-auth0.key | 27 + test/test-auth0.pem | 24 + test/test-decryption.key | 27 + test/utils.js | 84 ++ test/wsfed-result-wstrust13.xml | 16 + test/wsfed.tests.js | 154 ++++ 67 files changed, 7385 insertions(+) create mode 100644 .gitignore create mode 100644 .jshintrc create mode 100644 .npmignore create mode 100644 .npmrc create mode 100644 LICENSE create mode 100644 README.md create mode 100644 SECURITY-NOTICE.md create mode 100644 examples/auth0/app.js create mode 100644 examples/auth0/package.json create mode 100644 examples/auth0/views/account.ejs create mode 100644 examples/auth0/views/index.ejs create mode 100644 examples/auth0/views/layout.ejs create mode 100644 examples/login/app.js create mode 100644 examples/login/package.json create mode 100644 examples/login/views/account.ejs create mode 100644 examples/login/views/index.ejs create mode 100644 examples/login/views/layout.ejs create mode 100644 lib/passport-wsfed-saml2/errors/AuthenticationFailedError.js create mode 100644 lib/passport-wsfed-saml2/errors/SamlAssertionParserError.js create mode 100644 lib/passport-wsfed-saml2/errors/SamlResponseParserError.js create mode 100644 lib/passport-wsfed-saml2/errors/WSFederationResultParserError.js create mode 100644 lib/passport-wsfed-saml2/index.js create mode 100644 lib/passport-wsfed-saml2/saml.js create mode 100644 lib/passport-wsfed-saml2/samlp.js create mode 100644 lib/passport-wsfed-saml2/state/null.js create mode 100644 lib/passport-wsfed-saml2/state/session.js create mode 100644 lib/passport-wsfed-saml2/strategy.js create mode 100644 lib/passport-wsfed-saml2/templates.js create mode 100644 lib/passport-wsfed-saml2/templates/assert_and_destination.ejs create mode 100644 lib/passport-wsfed-saml2/templates/form.ejs create mode 100644 lib/passport-wsfed-saml2/templates/samlrequest.ejs create mode 100644 lib/passport-wsfed-saml2/utils.js create mode 100644 lib/passport-wsfed-saml2/wsfederation.js create mode 100644 package.json create mode 100644 test/fixture/samlp-server.js create mode 100644 test/fixture/wsfed-server.js create mode 100644 test/helpers.js create mode 100644 test/interop.tests.js create mode 100644 test/jwt.tests.js create mode 100644 test/saml11.tests.js create mode 100644 test/saml20.tests.js create mode 100644 test/samlp.functional.tests.js create mode 100644 test/samlp.tests.js create mode 100644 test/samples/encoded/samlrequest_signed_differentcert.txt create mode 100644 test/samples/encoded/samlresponse_encoded_xml.txt create mode 100644 test/samples/encoded/samlresponse_encrypted_and_signed.txt create mode 100644 test/samples/encoded/samlresponse_extraspace.txt create mode 100644 test/samples/encoded/samlresponse_signedassertion_dsprefix.txt create mode 100644 test/samples/encoded/samlresponse_utf8.txt create mode 100644 test/samples/plain/samlresponse_explicit_iso.txt create mode 100644 test/samples/plain/samlresponse_iso.txt create mode 100644 test/samples/plain/samlresponse_saml11_invalid_cert.txt create mode 100644 test/samples/plain/samlresponse_saml20_invalid_cert.txt create mode 100644 test/soap-fault-no-info.xml create mode 100644 test/soap-fault.xml create mode 100644 test/state/samlp.state.custom.tests.js create mode 100644 test/state/samlp.state.session.tests.js create mode 100644 test/state/wsfed.state.custom.tests.js create mode 100644 test/state/wsfed.state.session.tests.js create mode 100644 test/test-auth0-2.cer create mode 100644 test/test-auth0.cer create mode 100644 test/test-auth0.key create mode 100644 test/test-auth0.pem create mode 100644 test/test-decryption.key create mode 100644 test/utils.js create mode 100644 test/wsfed-result-wstrust13.xml create mode 100644 test/wsfed.tests.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3931da --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +.DS_Store +package-lock.json +*.log +coverage +.nyc_output diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..9de7a50 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,39 @@ +{ + "camelcase": false, + "curly": false, + + "node": true, + "esnext": true, + "bitwise": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": false, + "newcap": true, + "noarg": true, + "regexp": true, + "undef": true, + "strict": false, + "smarttabs": true, + "expr": true, + + "evil": true, + "browser": true, + "regexdash": true, + "wsh": true, + "trailing": true, + "sub": true, + "unused": true, + "laxcomma": true, + "nonbsp": true, + + "globals": { + "after": false, + "before": false, + "afterEach": false, + "beforeEach": false, + "describe": false, + "it": false, + "escape": false + } +} diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..6248e14 --- /dev/null +++ b/.npmignore @@ -0,0 +1,13 @@ +*.md +.DS_Store +.vscode +.git* +Makefile +docs/ +examples/ +support/ +test/ +coverage/ +.nyc_output/ +node_modules/ +package-lock.json diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0d65171 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Auth0, Inc. (http://auth0.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c9071b7 --- /dev/null +++ b/README.md @@ -0,0 +1,133 @@ +Passport-wsfed-saml2 +============= + +![Build Status](https://github.com/auth0/passport-wsfed-saml2/workflows/Tests/badge.svg) + +This is a ws-federation protocol + SAML2 tokens authentication provider for [Passport](http://passportjs.org/). + +The code was originally based on Henri Bergius's [passport-saml](https://github.com/bergie/passport-saml) library. + +Passport-wsfed-saml2 has been tested to work with both [Windows Azure Active Directory / Access Control Service](https://www.windowsazure.com/en-us/home/features/identity/) and with [Microsoft Active Directory Federation Services](http://en.wikipedia.org/wiki/Active_Directory_Federation_Services). + +## Installation + + $ npm install passport-wsfed-saml2 + +## Usage + +### Configure strategy + +This example utilizes a development namespace (auth10-dev) on [Windows Azure Access Control Service](https://www.windowsazure.com/en-us/home/features/identity/) and is using Google as the only identity provider configured for the sample application. + + +```javascript +passport.use(new wsfedsaml2( + { + path: '/login/callback', + realm: 'urn:node:app', + homeRealm: '', // optionally specify an identity provider to avoid showing the idp selector + identityProviderUrl: 'https://auth10-dev.accesscontrol.windows.net/v2/wsfederation', + cert: 'MIIDFjCCAf6gAwIBAgIQDRRprj9lv5RBvaQdlFltDzANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyRhdXRoMTAtZGV2LmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTEwOTIxMDMzMjMyWhcNMTIwOTIwMDkzMjMyWjAvMS0wKwYDVQQDEyRhdXRoMTAtZGV2LmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCEIAEB/KKT3ehNMy2MQEyJIQ14CnZ8DC2FZgL5Gw3UBSdRb9JinK/gw7yOQtwKfJUqeoZaUSAAdcdbgqwVxOnMBfWiYX7DGlEznSfqYVnjOWjqqjpoe0h6RaOkdWovDtoidmqVV1tWRJFjkj895clPxkLpnqqcycfXtSdZen0SroGyirD2mhMc9ccLbJ3zRnBNjlvpo5zow1zYows09tNC2EhGROL/OS4JNRQnJRICZC+WkA7Igf3xb4btJOzIPYhFiqCGrd/81CHmAyEuNzyc60I5yomDQfZ91Eb5Uk3F7mlfAlYB2aZwDwldLSOlVE8G1E5xFexF/5KyPC4ShNodAgMBAAGjLjAsMAsGA1UdDwQEAwIE8DAdBgNVHQ4EFgQUyYfx/r0czsPgTzitqey+fGMQpkcwDQYJKoZIhvcNAQEFBQADggEBAB5dgQlM3tKS+/cjlvMCPjZH0Iqo/Wxecri3YWi2iVziZ/TQ3dSV+J/iTyduN7rJmFQzTsNERcsgyAwblwnEKXXvlWo8G/+VDIMh3zVPNQFKns5WPkfkhoSVlnZPTQ8zdXAcWgDXbCgvdqIPozdgL+4l0W0XVL1ugA4/hmMXh4TyNd9Qj7MWvlmwVjevpSqN4wG735jAZFHb/L/vvc91uKqP+JvLNj8tPFVxatzi56X1V8jBM61Hx1Z9D0RCDjtmcQVysVEylW9O6mNy6ZrhLm0q5yecWudfBbTKDqRoCHQRjrMU2c5q/ZFDtgjLim7FaNxFbgTyjeRCPclEhfemYVg=' + }, + function(profile, done) { + findByEmail(profile.email, function(err, user) { + if (err) { + return done(err); + } + return done(null, user); + }); + }) +)); +``` + +### Provide the authentication callback + +You need to provide a route corresponding to the `path` configuration parameter given to the strategy: + +```javascript +app.post('/login/callback', + passport.authenticate('wsfed-saml2', { failureRedirect: '/', failureFlash: true }), + function(req, res) { + res.redirect('/'); + } +); +``` + +### Jwt + +Although this started as wsfed&saml we added support for wsfed&jwt. Usage is + +~~~javascript +passport.use(new wsfedsaml2( + { + jwt: { + //same options than node-jsonwebtoken + algorithm: 'RS256' + }, + cert: 'MIIDFjCCAf6gAwIBAgIQDRRprj9lv5RBvaQdlFltDzANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyRhdXRoMTAtZGV2LmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTEwOTIxMDMzMjMyWhcNMTIwOTIwMDkzMjMyWjAvMS0wKwYDVQQDEyRhdXRoMTAtZGV2LmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCEIAEB/KKT3ehNMy2MQEyJIQ14CnZ8DC2FZgL5Gw3UBSdRb9JinK/gw7yOQtwKfJUqeoZaUSAAdcdbgqwVxOnMBfWiYX7DGlEznSfqYVnjOWjqqjpoe0h6RaOkdWovDtoidmqVV1tWRJFjkj895clPxkLpnqqcycfXtSdZen0SroGyirD2mhMc9ccLbJ3zRnBNjlvpo5zow1zYows09tNC2EhGROL/OS4JNRQnJRICZC+WkA7Igf3xb4btJOzIPYhFiqCGrd/81CHmAyEuNzyc60I5yomDQfZ91Eb5Uk3F7mlfAlYB2aZwDwldLSOlVE8G1E5xFexF/5KyPC4ShNodAgMBAAGjLjAsMAsGA1UdDwQEAwIE8DAdBgNVHQ4EFgQUyYfx/r0czsPgTzitqey+fGMQpkcwDQYJKoZIhvcNAQEFBQADggEBAB5dgQlM3tKS+/cjlvMCPjZH0Iqo/Wxecri3YWi2iVziZ/TQ3dSV+J/iTyduN7rJmFQzTsNERcsgyAwblwnEKXXvlWo8G/+VDIMh3zVPNQFKns5WPkfkhoSVlnZPTQ8zdXAcWgDXbCgvdqIPozdgL+4l0W0XVL1ugA4/hmMXh4TyNd9Qj7MWvlmwVjevpSqN4wG735jAZFHb/L/vvc91uKqP+JvLNj8tPFVxatzi56X1V8jBM61Hx1Z9D0RCDjtmcQVysVEylW9O6mNy6ZrhLm0q5yecWudfBbTKDqRoCHQRjrMU2c5q/ZFDtgjLim7FaNxFbgTyjeRCPclEhfemYVg=' + }, + function(profile, done) { + findByEmail(profile.email, function(err, user) { + if (err) { + return done(err); + } + return done(null, user); + }); + }) +)); +~~~ + +### Configure strategy for ADFS (WS-Fed) + +This example utilizes a strategy with ADFS using WS-Fed. + +```javascript +passport.use('wsfed-saml2', new wsfedsaml2({ + // ADFS RP identifier + realm: 'urn:node:wsfedapp', + identityProviderUrl: 'https://my-adfs/adfs/ls', + // ADFS token signing certificate + thumbprint: '5D27....D27E' + // or cert: fs.readFileSync("adfs_signing_key.cer") +}, function (profile, done) { + // ... +})); + +``` + +### Configure strategy for ADFS (SAMLp) + +This example utilizes a strategy using SAMLp and RP token encryption. + +```javascript +passport.use('wsfed-saml2', new wsfedsaml2({ + // ADFS RP identifier + realm: 'urn:node:samlapp', + identityProviderUrl: 'https://my-adfs/adfs/ls', + // ADFS token signing certificate + thumbprint: '5D27...D27E', + // or cert: fs.readFileSync("adfs_signing_key.cer") + protocol: "samlp", + // This is the private key (use case where ADFS + // is configured for RP token encryption) + decryptionKey: fs.readFileSync("server.key") +}, function (profile, done) { + // ... +})); +``` + +## Issue Reporting + +If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. + +## Security Notice + +The [Security Notice](SECURITY-NOTICE.md) lists the version that is vulnerable and the actions that are required to upgrade to the latest version. + +## Author + +[Auth0](auth0.com) + +## License + +This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. diff --git a/SECURITY-NOTICE.md b/SECURITY-NOTICE.md new file mode 100644 index 0000000..39cd237 --- /dev/null +++ b/SECURITY-NOTICE.md @@ -0,0 +1,47 @@ +Security vulnerability details for passport-wsfed-saml2 < 3.0.10 +=============================================================== + +A vulnerability was found in the validation of a SAML signature. The validation doesn't ensure that the "Signature" tag is at the proper location inside an "Assertion" tag. This leads to a signature relocation attack where the attacker can corrupt one field of data while +maintaining the signature valid. This could allow an authenticated attacker to "remove" one group from his assertion or corrupt another field of an assertion. + +Updated packages are available on npm. To ensure delivery of additional bug fixes moving forward, please make sure your `package.json` file is updated to take patch and minor level updates of our libraries. See below: + +``` +{ + "dependencies": { + "passport-wsfed-saml2": "^3.0.10" + } +} +``` + +## Upgrade Notes + +This fix patches the library that your application runs, but will not impact your users, their current state, or any existing sessions. + +You can read more details regarding the vulnerability [here](https://auth0.com/docs/security/bulletins/cve-2018-8085). + + + +Security vulnerability details for passport-wsfed-saml2 < 3.0.5 +=============================================================== + +A vulnerability has been discovered in the passport-wsfed-saml2 library affecting versions < 3.0.5. This vulnerability allows an attacker to impersonate another user and potentially elevate their privileges if the SAML identity provider: + +* signs SAML response and signs assertion +* does not sign SAML response and signs assertion + +Developers using the passport-wsfed-saml2 Passport Strategy need to upgrade to the latest version: 3.0.5. + +Updated packages are available on npm. To ensure delivery of additional bug fixes moving forward, please make sure your `package.json` file is updated to take patch and minor level updates of our libraries. See below: + +``` +{ + "dependencies": { + "passport-wsfed-saml2": "^3.0.5" + } +} +``` + +## Upgrade Notes + +This fix patches the library that your application runs, but will not impact your users, their current state, or any existing sessions. diff --git a/examples/auth0/app.js b/examples/auth0/app.js new file mode 100644 index 0000000..418e732 --- /dev/null +++ b/examples/auth0/app.js @@ -0,0 +1,90 @@ +var express = require('express'); +var passport = require('passport'); +var Strategy = require('../../lib/passport-wsfed-saml2/index').Strategy; +var http = require('http'); + +passport.serializeUser(function(user, done) { + done(null, user); +}); + +passport.deserializeUser(function(id, done) { + done(null, id); +}); + +passport.use(new Strategy( + { + protocol: 'samlp', + path: '/login/callback', + realm: 'urn:saml-example', + homeRealm: '', + // identityProviderUrl: 'https://mdocs.auth0.com/samlp/dVrQZOG4gkBhzcLartSgW2v7kSnvW5XR?connection=github', + // thumbprint: 'c5b930896e3f4e2cc1d6d1ceb68f4d3de90deee6' + identityProviderUrl: 'https://login0.myauth0.com/samlp/wklezTET2P3iYA54Sraju8qFN0ohdI0G', + thumbprints: ['dba77ba142ff38d5076b4310700709c470d53790'] + }, function(profile, done) { + console.log("Auth with", profile); + if (!profile.email) { + return done(new Error("No email found"), null); + } + done(null, profile); + } +)); + +var app = express(); + +// configure Express +app.configure(function() { + app.set('views', __dirname + '/views'); + app.set('view engine', 'ejs'); + app.use(express.logger()); + app.use(express.cookieParser()); + app.use(express.bodyParser()); + app.use(express.methodOverride()); + app.use(express.session({ secret: 'keyboard cat' })); + app.use(passport.initialize()); + app.use(passport.session()); + app.use(app.router); + app.use(express.static(__dirname + '/../../public')); +}); + + +app.get('/', function(req, res){ + res.render('index', { user: req.user }); +}); + +app.get('/account', ensureAuthenticated, function(req, res){ + res.render('account', { user: req.user }); +}); + +app.get('/login', + passport.authenticate('wsfed-saml2', { failureRedirect: '/', forceAuthn: true }), + function(req, res) { + res.redirect('/'); + } +); + +app.post('/login/callback', + passport.authenticate('wsfed-saml2', { failureRedirect: '/' }), + function(req, res) { + res.redirect('/'); + } +); + +app.get('/logout', function(req, res){ + req.logout(); + res.redirect('/'); +}); + +http.createServer(app).listen(3000, function () { + console.log("Server listening in http://localhost:3000"); +}); + +// Simple route middleware to ensure user is authenticated. +// Use this route middleware on any resource that needs to be protected. If +// the request is authenticated (typically via a persistent login session), +// the request will proceed. Otherwise, the user will be redirected to the +// login page. +function ensureAuthenticated(req, res, next) { + if (req.isAuthenticated()) { return next(); } + res.redirect('/login'); +} \ No newline at end of file diff --git a/examples/auth0/package.json b/examples/auth0/package.json new file mode 100644 index 0000000..7a3e06f --- /dev/null +++ b/examples/auth0/package.json @@ -0,0 +1,10 @@ +{ + "name": "passport-azure-acs-sample", + "version": "0.0.0", + "dependencies": { + "express": ">= 0.0.0", + "ejs": ">= 0.0.0", + "passport": ">= 0.0.0", + "passport-azure-acs": ">= 0.0.0" + } +} diff --git a/examples/auth0/views/account.ejs b/examples/auth0/views/account.ejs new file mode 100644 index 0000000..758ba25 --- /dev/null +++ b/examples/auth0/views/account.ejs @@ -0,0 +1,2 @@ +

ID: <%= user.id %>

+

Name: <%= user.displayName %>

diff --git a/examples/auth0/views/index.ejs b/examples/auth0/views/index.ejs new file mode 100644 index 0000000..7ee22e2 --- /dev/null +++ b/examples/auth0/views/index.ejs @@ -0,0 +1,5 @@ +<% if (!user) { %> +

Welcome! Please log in.

+<% } else { %> +

Hello, <%= user.email %>.

+<% } %> \ No newline at end of file diff --git a/examples/auth0/views/layout.ejs b/examples/auth0/views/layout.ejs new file mode 100644 index 0000000..d7df87e --- /dev/null +++ b/examples/auth0/views/layout.ejs @@ -0,0 +1,21 @@ + + + + Passport-Windows Azure Access Control Service Example + + + <% if (!user) { %> +

+ Home | + Log In +

+ <% } else { %> +

+ Home | + Account | + Log Out +

+ <% } %> + <%- body %> + + \ No newline at end of file diff --git a/examples/login/app.js b/examples/login/app.js new file mode 100644 index 0000000..385184d --- /dev/null +++ b/examples/login/app.js @@ -0,0 +1,144 @@ +// var express = require('express') +// , passport = require('passport') +// , util = require('util') +// , wsfedsaml2 = require('../../lib/passport-wsfed-saml2/index').Strategy +// , fs = require('fs'); + +var express = require('express') + , passport = require('passport') + , util = require('util') + , wsfedsaml2 = require('../../lib/passport-wsfed-saml2/index').Strategy + , fs = require('fs') + , morgan = require('morgan') + , cookieParser = require('cookie-parser') + , bodyParser = require('body-parser') + , methodOverride = require('method-override') + , session = require('express-session'); + +var users = [ + { id: 1, givenName: 'matias', email: 'matias@auth10.com' } + , { id: 2, givenName: 'foo', email: 'foo@gmail.com' } +]; + +function findByEmail(email, fn) { + for (var i = 0, len = users.length; i < len; i++) { + var user = users[i]; + if (user.email === email) { + return fn(null, user); + } + } + return fn(null, null); +} + + +// Passport session setup. +// To support persistent login sessions, Passport needs to be able to +// serialize users into and deserialize users out of the session. Typically, +// this will be as simple as storing the user ID when serializing, and finding +// the user by ID when deserializing. +passport.serializeUser(function(user, done) { + done(null, user.email); +}); + +passport.deserializeUser(function(id, done) { + findByEmail(id, function (err, user) { + done(err, user); + }); +}); + +passport.use(new wsfedsaml2( + { + path: '/login/callback', + realm: 'urn:node:app', + homeRealm: '', // specify an identity provider to avoid showing the idp selector + identityProviderUrl: 'https://auth10-dev.accesscontrol.windows.net/v2/wsfederation', + // setup either a certificate base64 encoded (cer) or just the thumbprint of the certificate if public key is embedded in the signature + + //cert: 'MIIDFjCCAf6gAwIBAgIQDRRprj9lv5RBvaQdlFltDzANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyRhdXRoMTAtZGV2LmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTEwOTIxMDMzMjMyWhcNMTIwOTIwMDkzMjMyWjAvMS0wKwYDVQQDEyRhdXRoMTAtZGV2LmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCEIAEB/KKT3ehNMy2MQEyJIQ14CnZ8DC2FZgL5Gw3UBSdRb9JinK/gw7yOQtwKfJUqeoZaUSAAdcdbgqwVxOnMBfWiYX7DGlEznSfqYVnjOWjqqjpoe0h6RaOkdWovDtoidmqVV1tWRJFjkj895clPxkLpnqqcycfXtSdZen0SroGyirD2mhMc9ccLbJ3zRnBNjlvpo5zow1zYows09tNC2EhGROL/OS4JNRQnJRICZC+WkA7Igf3xb4btJOzIPYhFiqCGrd/81CHmAyEuNzyc60I5yomDQfZ91Eb5Uk3F7mlfAlYB2aZwDwldLSOlVE8G1E5xFexF/5KyPC4ShNodAgMBAAGjLjAsMAsGA1UdDwQEAwIE8DAdBgNVHQ4EFgQUyYfx/r0czsPgTzitqey+fGMQpkcwDQYJKoZIhvcNAQEFBQADggEBAB5dgQlM3tKS+/cjlvMCPjZH0Iqo/Wxecri3YWi2iVziZ/TQ3dSV+J/iTyduN7rJmFQzTsNERcsgyAwblwnEKXXvlWo8G/+VDIMh3zVPNQFKns5WPkfkhoSVlnZPTQ8zdXAcWgDXbCgvdqIPozdgL+4l0W0XVL1ugA4/hmMXh4TyNd9Qj7MWvlmwVjevpSqN4wG735jAZFHb/L/vvc91uKqP+JvLNj8tPFVxatzi56X1V8jBM61Hx1Z9D0RCDjtmcQVysVEylW9O6mNy6ZrhLm0q5yecWudfBbTKDqRoCHQRjrMU2c5q/ZFDtgjLim7FaNxFbgTyjeRCPclEhfemYVg=' + thumbprints: ['a3cff17cbf7e793a97861390eb698d00e9598537'] + }, + function(profile, done) { + console.log("Auth with", profile); + if (!profile.email) { + return done(new Error("No email found"), null); + } + // asynchronous verification, for effect... + process.nextTick(function () { + findByEmail(profile.email, function(err, user) { + if (err) { + return done(err); + } + if (!user) { + // "Auto-registration" + users.push(profile); + return done(null, profile); + } + return done(null, user); + }) + }); + } +)); + +var app = express(); +var router = express.Router(); + +// configure Express +app.set('views', __dirname + '/views'); +app.set('view engine', 'ejs'); +app.use(morgan('combined')); +app.use(cookieParser()); +app.use(bodyParser.urlencoded({ + extended: true +})); +app.use(methodOverride()); +app.use(session({ + secret: 'keyboard cat', + resave: false, + saveUninitialized: true +})); +app.use(passport.initialize()); +app.use(passport.session()); +app.use('', router); +app.use(express.static(__dirname + '/../../public')); + + +app.get('/', function(req, res){ + res.render('index', { user: req.user }); +}); + +app.get('/account', ensureAuthenticated, function(req, res){ + res.render('account', { user: req.user }); +}); + +app.get('/login', + passport.authenticate('wsfed-saml2', { failureRedirect: '/', failureFlash: true }), + function(req, res) { + res.redirect('/'); + } +); + +app.post('/login/callback', + passport.authenticate('wsfed-saml2', { failureRedirect: '/', failureFlash: true }), + function(req, res) { + res.redirect('/'); + } +); + +app.get('/logout', function(req, res){ + req.logout(); + res.redirect('/'); +}); + +app.listen(3000, function () { + console.log("Server listening in http://localhost:3000"); +}); + +// Simple route middleware to ensure user is authenticated. +// Use this route middleware on any resource that needs to be protected. If +// the request is authenticated (typically via a persistent login session), +// the request will proceed. Otherwise, the user will be redirected to the +// login page. +function ensureAuthenticated(req, res, next) { + if (req.isAuthenticated()) { return next(); } + res.redirect('/login') +} \ No newline at end of file diff --git a/examples/login/package.json b/examples/login/package.json new file mode 100644 index 0000000..1b98462 --- /dev/null +++ b/examples/login/package.json @@ -0,0 +1,14 @@ +{ + "name": "passport-azure-acs-sample", + "version": "0.0.0", + "dependencies": { + "body-parser": "^1.10.1", + "cookie-parser": "^1.3.3", + "ejs": "^2.1.4", + "express": "^4.11.0", + "express-session": "^1.10.1", + "method-override": "^2.3.1", + "morgan": "^1.5.1", + "passport": ">= 0.0.0" + } +} diff --git a/examples/login/views/account.ejs b/examples/login/views/account.ejs new file mode 100644 index 0000000..758ba25 --- /dev/null +++ b/examples/login/views/account.ejs @@ -0,0 +1,2 @@ +

ID: <%= user.id %>

+

Name: <%= user.displayName %>

diff --git a/examples/login/views/index.ejs b/examples/login/views/index.ejs new file mode 100644 index 0000000..a1f0d4d --- /dev/null +++ b/examples/login/views/index.ejs @@ -0,0 +1,5 @@ +<% if (!user) { %> +

Welcome! Please log in.

+<% } else { %> +

Hello, <%= user.givenName %>.

+<% } %> \ No newline at end of file diff --git a/examples/login/views/layout.ejs b/examples/login/views/layout.ejs new file mode 100644 index 0000000..d7df87e --- /dev/null +++ b/examples/login/views/layout.ejs @@ -0,0 +1,21 @@ + + + + Passport-Windows Azure Access Control Service Example + + + <% if (!user) { %> +

+ Home | + Log In +

+ <% } else { %> +

+ Home | + Account | + Log Out +

+ <% } %> + <%- body %> + + \ No newline at end of file diff --git a/lib/passport-wsfed-saml2/errors/AuthenticationFailedError.js b/lib/passport-wsfed-saml2/errors/AuthenticationFailedError.js new file mode 100644 index 0000000..cd1ebdd --- /dev/null +++ b/lib/passport-wsfed-saml2/errors/AuthenticationFailedError.js @@ -0,0 +1,13 @@ +function AuthenticationFailedError (message, detail, status) { + var err = Error.call(this, message); + err.name = 'AuthenticationFailedError'; + err.message = message || 'Authentication Failed'; + err.detail = detail; + err.status = status || 401; + return err; +} + +AuthenticationFailedError.prototype = Object.create(Error.prototype); +AuthenticationFailedError.prototype.constructor = AuthenticationFailedError; + +module.exports = AuthenticationFailedError; diff --git a/lib/passport-wsfed-saml2/errors/SamlAssertionParserError.js b/lib/passport-wsfed-saml2/errors/SamlAssertionParserError.js new file mode 100644 index 0000000..84db458 --- /dev/null +++ b/lib/passport-wsfed-saml2/errors/SamlAssertionParserError.js @@ -0,0 +1,13 @@ +function SamlAssertionParserError (message, detail, status) { + var err = Error.call(this, message); + err.name = 'SamlAssertionParserError'; + err.message = message || 'Error parsing SAML Assertion'; + err.detail = detail; + err.status = status || 400; + return err; +} + +SamlAssertionParserError.prototype = Object.create(Error.prototype); +SamlAssertionParserError.prototype.constructor = SamlAssertionParserError; + +module.exports = SamlAssertionParserError; diff --git a/lib/passport-wsfed-saml2/errors/SamlResponseParserError.js b/lib/passport-wsfed-saml2/errors/SamlResponseParserError.js new file mode 100644 index 0000000..657831f --- /dev/null +++ b/lib/passport-wsfed-saml2/errors/SamlResponseParserError.js @@ -0,0 +1,13 @@ +function SamlResponseParserError (message, detail, status) { + var err = Error.call(this, message); + err.name = 'SamlResponseParserError'; + err.message = message || 'Error parsing SAMLResponse'; + err.detail = detail; + err.status = status || 400; + return err; +} + +SamlResponseParserError.prototype = Object.create(Error.prototype); +SamlResponseParserError.prototype.constructor = SamlResponseParserError; + +module.exports = SamlResponseParserError; diff --git a/lib/passport-wsfed-saml2/errors/WSFederationResultParserError.js b/lib/passport-wsfed-saml2/errors/WSFederationResultParserError.js new file mode 100644 index 0000000..e7a3f30 --- /dev/null +++ b/lib/passport-wsfed-saml2/errors/WSFederationResultParserError.js @@ -0,0 +1,13 @@ +function WSFederationResultParseError (message, detail, status) { + var err = Error.call(this, message); + err.name = 'WSFederationResultParseError'; + err.message = message || 'Error parsing wresult'; + err.detail = detail; + err.status = status || 400; + return err; +} + +WSFederationResultParseError.prototype = Object.create(Error.prototype); +WSFederationResultParseError.prototype.constructor = WSFederationResultParseError; + +module.exports = WSFederationResultParseError; diff --git a/lib/passport-wsfed-saml2/index.js b/lib/passport-wsfed-saml2/index.js new file mode 100644 index 0000000..1347a5a --- /dev/null +++ b/lib/passport-wsfed-saml2/index.js @@ -0,0 +1,8 @@ +exports.Strategy = require('./strategy'); +exports.SAML = require('./saml'); +exports.samlp = require('./samlp'); + +exports.AuthenticationFailedError = require('./errors/AuthenticationFailedError'); +exports.SamlAssertionParserError = require('./errors/SamlAssertionParserError'); +exports.SamlResponseParserError = require('./errors/SamlResponseParserError'); +exports.WSFederationResultParserError = require('./errors/WSFederationResultParserError'); diff --git a/lib/passport-wsfed-saml2/saml.js b/lib/passport-wsfed-saml2/saml.js new file mode 100644 index 0000000..2e2f8bd --- /dev/null +++ b/lib/passport-wsfed-saml2/saml.js @@ -0,0 +1,429 @@ +// credits to: https://github.com/bergie/passport-saml + +var crypto = require('crypto'); +var xpath = require('xpath'); +var xmlCrypto = require('xml-crypto'); +var EventEmitter = require('events'); +const forge = require('node-forge'); +const utils = require('./utils'); + +var ELEMENT_NODE = 1; + +var SAML = function (options) { + this.options = options || {}; + + if (this.options.thumbprint) { + this.options.thumbprints = (this.options.thumbprints || []).concat([this.options.thumbprint]); + } + + if (!this.options.cert && (!this.options.thumbprints || this.options.thumbprints.length === 0)) { + throw new Error('You should set either a base64 encoded certificate or the thumbprints of the signing certificates'); + } + + this.options.checkExpiration = (typeof this.options.checkExpiration !== 'undefined') ? this.options.checkExpiration : true; + // Note! It would be best to set this to true. But it's defaulting to false so as not to break login for expired certs. + this.options.checkCertExpiration = (typeof this.options.checkCertExpiration !== 'undefined') ? this.options.checkCertExpiration : false; + //clockskew in minutes + this.options.clockSkew = (typeof this.options.clockSkew === 'number' && this.options.clockSkew >= 0) ? this.options.clockSkew : 3; + this.options.checkAudience = (typeof this.options.checkAudience !== 'undefined') ? this.options.checkAudience : true; + this.options.checkRecipient = (typeof this.options.checkRecipient !== 'undefined') ? this.options.checkRecipient : true; + this.options.checkNameQualifier = (typeof this.options.checkNameQualifier !== 'undefined') ? this.options.checkNameQualifier : true; + this.options.checkSPNameQualifier = (typeof this.options.checkSPNameQualifier !== 'undefined') ? this.options.checkSPNameQualifier : true; + this.eventEmitter = this.options.eventEmitter || new EventEmitter(); +}; + +SAML.prototype.validateSignature = function (xml, options, callback) { + var self = this; + xml = utils.parseSamlResponse(xml); + + var signaturePath = this.options.signaturePath || options.signaturePath; + var signatures = xpath.select(signaturePath, xml); + if (signatures.length === 0) { + return callback(new Error('Signature is missing (xpath: ' + signaturePath + ')')); + } else if (signatures.length > 1) { + return callback(new Error('Signature was found more than one time (xpath: ' + signaturePath + ')')); + } + var signature = signatures[0]; + + var sig = new xmlCrypto.SignedXml(null, { idAttribute: 'AssertionID' }); + sig.keyInfoProvider = { + getKeyInfo: function (key) { + return ""; + }, + getKey: function (keyInfo) { + + //If there's no embedded signing cert, use the configured cert through options + if(!keyInfo || keyInfo.length===0){ + if(!options.cert) throw new Error('options.cert must be specified for SAMLResponses with no embedded signing certificate'); + return utils.certToPEM(options.cert); + } + + //If there's an embedded signature and thumprints are provided check that + if (options.thumbprints && options.thumbprints.length > 0) { + var embeddedSignature = keyInfo[0].getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "X509Certificate"); + if (embeddedSignature.length > 0) { + var base64cer = embeddedSignature[0].firstChild.toString(); + var shasum = crypto.createHash('sha1'); + var der = new Buffer(base64cer, 'base64').toString('binary'); + shasum.update(der, 'latin1'); + self.calculatedThumbprint = shasum.digest('hex'); + + // using embedded cert, so options.cert is not used anymore + delete options.cert; + return utils.certToPEM(base64cer); + } + } + + // If there's an embedded signature, but no thumprints are supplied, use options.cert + // either options.cert or options.thumbprints must be specified so at this point there + // must be an options.cert + return utils.certToPEM(options.cert); + } + }; + + var valid; + + try { + sig.loadSignature(signature); + valid = sig.checkSignature(xml.toString()); + + if (!self.extractAndValidateCertExpiration(xml, options.cert) && self.options.checkCertExpiration) { + return callback(new Error('The signing certificate is not currently valid.'), null); + } + } catch (e) { + if (e.message === 'PEM_read_bio_PUBKEY failed') { + return callback(new Error('The signing certificate is invalid (' + e.message + ')')); + } + if (e.opensslErrorStack !== undefined) { + const err = new Error(`The signing certificate is invalid (${e.opensslErrorStack.join(', ')})`); + err.originalError = e; + + return callback(err); + } + + return callback(e); + } + + if (!valid) { + return callback(new Error('Signature check errors: ' + sig.validationErrors)); + } + + if (options.cert) { + return callback(); + } + + if (options.thumbprints) { + + var valid_thumbprint = options.thumbprints.some(function (thumbprint) { + return self.calculatedThumbprint.toUpperCase() === thumbprint.toUpperCase(); + }); + + if (!valid_thumbprint) { + return callback(new Error('Invalid thumbprint (configured: ' + options.thumbprints.join(', ').toUpperCase() + '. calculated: ' + this.calculatedThumbprint.toUpperCase() + ')' )); + } + + return callback(); + } +}; + +SAML.prototype.extractAndValidateCertExpiration = function (validatedSamlAssertion, optionsCert) { + // This accepts a validated SAML assertion and checks current time against the valid cert dates + const certNodes = validatedSamlAssertion.getElementsByTagName("X509Certificate"); + + const cert = certNodes.length > 0 ? certNodes[0].textContent : optionsCert; + + if (!cert) { return false; } + + const parsedCert = forge.pki.certificateFromPem(utils.certToPEM(cert)); + + const nowDate = new Date(); + + // true if current date is before expiry AND after cert start date + if ( ! (nowDate > parsedCert.validity.notBefore && nowDate < parsedCert.validity.notAfter)) { + this.eventEmitter.emit('certificateExpirationValidationFailed', {}); + return false; + } + + return true; +}; + +SAML.prototype.validateExpiration = function (samlAssertion, version) { + var self = this; + var conditions = xpath.select(".//*[local-name(.)='Conditions']", samlAssertion); + if (!conditions || conditions.length === 0) return true; + + var notBefore = new Date(conditions[0].getAttribute('NotBefore')); + notBefore = notBefore.setMinutes(notBefore.getMinutes() - self.options.clockSkew); + + var notOnOrAfter = new Date(conditions[0].getAttribute('NotOnOrAfter')); + notOnOrAfter = notOnOrAfter.setMinutes(notOnOrAfter.getMinutes() + self.options.clockSkew); + var now = new Date(); + + if (now < notBefore || now > notOnOrAfter) + return false; + + return true; +}; + +SAML.prototype.validateAudience = function (samlAssertion, realm, version) { + var audience; + if (version === '2.0') { + audience = xpath.select(".//*[local-name(.)='Conditions']/*[local-name(.)='AudienceRestriction']/*[local-name(.)='Audience']", samlAssertion); + } else { + audience = xpath.select(".//*[local-name(.)='Conditions']/*[local-name(.)='AudienceRestrictionCondition']/*[local-name(.)='Audience']", samlAssertion); + } + + if (!audience || audience.length === 0) return false; + return utils.stringCompare(audience[0].textContent, realm); +}; + +SAML.prototype.validateNameQualifier = function (samlAssertion, issuer) { + var nameID = getNameID20(samlAssertion); + // NameQualifier is optional. Only validate if exists + if (!nameID || !nameID.Format || !nameID.NameQualifier) return true; + + if ([ + 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', + 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + ].indexOf(nameID.Format) == -1){ + // Ignore validation if the format is not persistent or transient + return true; + } + + return nameID.NameQualifier === issuer +}; + +SAML.prototype.validateSPNameQualifier = function (samlAssertion, audience) { + var nameID = getNameID20(samlAssertion); + // SPNameQualifier is optional. Only validate if exists + if (!nameID || !nameID.Format || !nameID.SPNameQualifier) return true; + + if ([ + 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', + 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + ].indexOf(nameID.Format) == -1){ + // Ignore validation if the format is not persistent or transient + return true; + } + + return nameID.SPNameQualifier === audience +}; + +// https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf +// Page 19 of 91 +// Recipient [Optional] +// A URI specifying the entity or location to which an attesting entity can present the assertion. For +// example, this attribute might indicate that the assertion must be delivered to a particular network +// endpoint in order to prevent an intermediary from redirecting it someplace else. +SAML.prototype.validateRecipient = function(samlAssertion, recipientUrl){ + var subjectConfirmationData = xpath.select(".//*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']", samlAssertion); + + // subjectConfirmationData is optional in the spec. Only validate if the assertion contains a recipient + if (!subjectConfirmationData || subjectConfirmationData.length === 0){ + return true; + } + + var recipient = subjectConfirmationData[0].getAttribute('Recipient'); + + var valid = !recipient || recipient === recipientUrl; + + if (!valid){ + this.eventEmitter.emit('recipientValidationFailed', { + configuredRecipient: recipientUrl, + assertionRecipient: recipient + }); + } + + return valid; +}; + +SAML.prototype.parseAttributes = function (samlAssertion, version) { + function getAttributes(samlAssertion) { + var attributes = xpath.select(".//*[local-name(.)='AttributeStatement']/*[local-name(.)='Attribute']", samlAssertion); + return attributes; + } + + function getSessionIndex(samlAssertion) { + var authnStatement = xpath.select(".//*[local-name(.)='AuthnStatement']", samlAssertion); + var sessionIndex = authnStatement.length > 0 && authnStatement[0].attributes.length > 0 ? + authnStatement[0].getAttribute('SessionIndex') : undefined; + return sessionIndex || undefined; + } + + function getNameID11(samlAssertion) { + var nameId = xpath.select(".//*[local-name(.)='AuthenticationStatement']/*[local-name(.)='Subject']/*[local-name(.)='NameIdentifier']", samlAssertion); + + if (nameId.length === 0) { + // only for backward compatibility with adfs + nameId = xpath.select(".//*[local-name(.)='AttributeStatement']/*[local-name(.)='Subject']/*[local-name(.)='NameIdentifier']", samlAssertion); + if (nameId.length === 0) return; + } + + return nameId[0].textContent; + } + + function getAttributeValues(attribute) { + if (!attribute || attribute.childNodes.length === 0) return; + var attributeValues = []; + for (var i = 0; i 1) { + profile['nameIdAttributes'] = nameId; + } + } + + authContext = getAuthContext20(samlAssertion); + if (authContext) { + profile['http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod'] = authContext; + } + + } else { + if (attributes) { + for (var index2 in attributes) { + var attribute2 = attributes[index2]; + var value2 = getAttributeValues(attribute2); + profile[attribute2.getAttribute('AttributeNamespace') + '/' + attribute2.getAttribute('AttributeName')] = value2; + } + } + + nameId = getNameID11(samlAssertion); + if (nameId) { + profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'] = typeof nameId === 'string' ? nameId : nameId['#']; + } + } + + return profile; +}; + +SAML.prototype.validateSamlAssertion = function (samlAssertion, callback) { + var self = this; + + samlAssertion = utils.parseSamlResponse(samlAssertion); + + self.validateSignature(samlAssertion.toString(), { + cert: self.options.cert, + thumbprints: self.options.thumbprints, + signaturePath: "/*[local-name(.)='Assertion']/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']" }, function(err) { + if (err) return callback(err); + + self.parseAssertion(samlAssertion, callback); + }); +}; + +SAML.prototype.parseAssertion = function(samlAssertion, callback) { + var self = this; + if (self.options.extractSAMLAssertion){ + samlAssertion = self.options.extractSAMLAssertion(samlAssertion); + } + + samlAssertion = utils.parseSamlResponse(samlAssertion); + + if (!samlAssertion.getAttribute) + samlAssertion = samlAssertion.documentElement; + + const version = utils.getSamlAssertionVersion(samlAssertion); + if (!version){ + // Note that this assumes any version returned by getSamlAssertionVersion is supported. + return callback(new Error('SAML Assertion version not supported, or not defined'), null); + } + + if (self.options.checkExpiration && !self.validateExpiration(samlAssertion, version)) { + return callback(new Error('assertion has expired.'), null); + } + + if (self.options.checkAudience && !self.validateAudience(samlAssertion, self.options.realm, version)) { + return callback(new Error('Audience is invalid. Configured: ' + self.options.realm), null); + } + + if (!self.validateRecipient(samlAssertion, self.options.recipientUrl)) { + if (self.options.checkRecipient){ + return callback(new Error('Recipient is invalid. Configured: ' + self.options.recipientUrl), null); + } + } + + var profile = self.parseAttributes(samlAssertion, version); + + var issuer; + if (version === '2.0') { + var issuerNode = xpath.select(".//*[local-name(.)='Issuer']", samlAssertion); + if (issuerNode.length > 0) issuer = issuerNode[0].textContent; + } else { + issuer = samlAssertion.getAttribute('Issuer'); + } + + + this.eventEmitter.emit('parseAssertion', { + issuer: issuer, + version: version, + }); + + profile.issuer = issuer; + + // Validate the name qualifier in the NameID element if found with the audience + if (self.options.checkNameQualifier && !self.validateNameQualifier(samlAssertion, issuer)){ + return callback(new Error('NameQualifier attribute in the NameID element does not match ' + issuer), null); + } + + // Validate the SP name qualifier in the NameID element if found with the issuer + if (self.options.checkSPNameQualifier && !self.validateSPNameQualifier(samlAssertion, self.options.realm)){ + return callback(new Error('SPNameQualifier attribute in the NameID element does not match ' + self.options.realm), null); + } + + if (!profile.email && profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']) { + profile.email = profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']; + } + + callback(null, profile); +}; + +function getNameID20(samlAssertion) { + var nameId = xpath.select(".//*[local-name(.)='Subject']/*[local-name(.)='NameID']", samlAssertion); + if (nameId.length === 0) return; + var element = nameId[0]; + var result = { + value: element.textContent, + }; + + ['NameQualifier', + 'SPNameQualifier', + 'Format', + 'SPProvidedID'].forEach(function(key) { + var value = element.getAttribute(key); + if (!value) return; + result[key] = element.getAttribute(key); + }); + + return result; +} + +exports.SAML = SAML; diff --git a/lib/passport-wsfed-saml2/samlp.js b/lib/passport-wsfed-saml2/samlp.js new file mode 100644 index 0000000..3683b2c --- /dev/null +++ b/lib/passport-wsfed-saml2/samlp.js @@ -0,0 +1,503 @@ +var xpath = require('xpath'); +var qs = require('querystring'); +var zlib = require('zlib'); +var xtend = require('xtend'); +var url = require('url'); +var xmlenc = require('xml-encryption'); +var crypto = require('crypto'); +var querystring = require('querystring'); +var SignedXml = require('xml-crypto').SignedXml; +var templates = require('./templates'); +var EventEmitter = require('events'); +var validUrl = require('valid-url'); + +var utils = require('./utils'); +var AuthenticationFailedError = require('./errors/AuthenticationFailedError'); + +var BINDINGS = { + HTTP_POST: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + HTTP_REDIRECT: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' +}; + +var ErrorMessages = { + 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch': 'The SAML responder could not process the request because the version of the request message was incorrect.', + 'urn:oasis:names:tc:SAML:2.0:status:Requester' : 'The request could not be performed due to an error on the part of the requester', + 'urn:oasis:names:tc:SAML:2.0:status:Responder' : 'The request could not be performed due to an error on the part of the SAML responder or SAML authority', + 'urn:oasis:names:tc:SAML:2.0:status:AuthnFailed' : 'The responding provider was unable to successfully authenticate the principal' +}; + +function ignoreValidationFunction(samlResponseID, done){ + return done(); +} + +var encodingMappings = { + 'ISO-8859-1': 'binary', + 'UTF-8': 'utf8' +}; + +var Samlp = module.exports = function Samlp (options, saml) { + this.options = options || {}; + + if (this.options.thumbprint) { + this.options.thumbprints = (this.options.thumbprints || []).concat([this.options.thumbprint]); + } + + if (typeof options.deflate === 'undefined') { + this.options.deflate = true; + } + + this.options.checkDestination = (typeof this.options.checkDestination !== 'undefined') ? this.options.checkDestination : true; + this.options.checkResponseID = (typeof this.options.checkResponseID !== 'undefined') ? this.options.checkResponseID : true; + this.options.checkInResponseTo = (typeof this.options.checkInResponseTo !== 'undefined') ? this.options.checkInResponseTo : true; + + this.eventEmitter = this.options.eventEmitter || new EventEmitter(); + this._saml = saml; + + this.isValidResponseID = this.options.isValidResponseID || ignoreValidationFunction; + this.isValidInResponseTo = this.options.isValidInResponseTo || ignoreValidationFunction; + + this.default_encoding = encodingMappings[this.options.default_encoding] || 'utf8'; +}; + +function getProp(obj, path) { + return path.split('.').reduce(function (prev, curr) { + return prev[curr]; + }, obj); +} + +var supplant = function (tmpl, o) { + return tmpl.replace(/\@\@([^\@]*)\@\@/g, + function (a, b) { + var r = getProp(o, b); + return typeof r === 'string' || typeof r === 'number' ? r : a; + } + ); +}; + +var trimXml = function (xml) { + return xml.replace(/\r\n/g, '') + .replace(/\n/g,'') + .replace(/>(\s*)<') //unindent + .trim(); +}; + +var removeHeaders = function (cert) { + var pem = /-----BEGIN (\w*)-----([^-]*)-----END (\w*)-----/g.exec(cert.toString()); + if (pem && pem.length > 0) { + return pem[2].replace(/[\n|\r\n]/g, ''); + } + return null; +}; + +var sign = function (content, key, algorithm) { + var signer = crypto.createSign(algorithm.toUpperCase()); + signer.update(content, 'latin1'); + return signer.sign(key, 'base64'); +}; + +var algorithms = { + signature: { + 'rsa-sha256': 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', + 'rsa-sha1': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' + }, + digest: { + 'sha256': 'http://www.w3.org/2001/04/xmlenc#sha256', + 'sha1': 'http://www.w3.org/2000/09/xmldsig#sha1' + } +}; + +function collectAncestorNamespaces(node, nameSpaces = [], maxDeep = 5){ + if (!(node && node.parentNode) || maxDeep <= 0) { + return nameSpaces; + } + + const parent = node.parentNode; + + if(parent.attributes && parent.attributes.length > 0){ + for(let i=0;i' + removeHeaders(options.signingKey.cert) + ''; + } + }; + + sig.signingKey = options.signingKey.key; + try { + sig.computeSignature(SAMLRequest, { location: { reference: "//*[local-name(.)='Issuer']", action: 'after' } }); // Signature element must be located after Issuer + } catch (e) { + return callback(new Error('fail to compute signature')); + } + + SAMLRequest = trimXml(sig.getSignedXml()); + } + + params.SAMLRequest = new Buffer(SAMLRequest).toString('base64'); + return callback(null, params); + } + + // HTTP-Redirect with deflate encoding (http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf - section 3.4.4.1) + zlib.deflateRaw(new Buffer(SAMLRequest), function (err, buffer) { + if (err) return callback(err); + + params.SAMLRequest = buffer.toString('base64'); + + if (options.signingKey) { + // construct the Signature: a string consisting of the concatenation of the SAMLRequest, + // RelayState (if present) and SigAlg query string parameters (each one URLencoded) + if (params.RelayState === '') { + // if there is no RelayState value, the parameter should be omitted from the signature computation + delete params.RelayState; + } + + params.SigAlg = algorithms.signature[signatureAlgorithm]; + + try { + params.Signature = sign(querystring.stringify(params), options.signingKey.key, signatureAlgorithm); + } + catch(e) { + return callback(e); + } + } + + callback(null, params); + }); + }, + + getSamlRequestUrl: function (opts, callback) { + var options = xtend(opts || {}, this.options); + + this.getSamlRequestParams(options, function (err, params) { + if (err) return callback(err); + + var parsedUrl = url.parse(options.identityProviderUrl, true); + var samlRequestUrl = options.identityProviderUrl.split('?')[0] + '?' + qs.encode(xtend(parsedUrl.query, params)); + return callback(null, samlRequestUrl); + }); + }, + + getSamlRequestForm: function (opts, callback) { + var options = xtend(opts || {}, this.options); + + this.getSamlRequestParams(options, function (err, params) { + if (err) return callback(err); + + return callback(null, templates.form({ + postUrl: options.identityProviderUrl, + RelayState: params.RelayState, + SAMLRequest: params.SAMLRequest + })); + }); + }, + + decodeResponse: function(req) { + var decoded = new Buffer(req.body['SAMLResponse'], 'base64').toString(this.default_encoding); + + const encoding = utils.getEncoding(decoded); + if (encoding && encodingMappings[encoding] && encodingMappings[encoding] !== this.default_encoding){ + // Encoding defers from the one configured, decode again with the correct value + decoded = new Buffer(req.body['SAMLResponse'], 'base64').toString(encodingMappings[encoding]); + } + + return decoded; + }, + + extractAssertion: function(samlpResponse, callback) { + samlpResponse = utils.parseSamlResponse(samlpResponse); + const saml2Namespace = 'urn:oasis:names:tc:SAML:2.0:assertion'; + + function done(err, assertion) { + if (err) { + return callback(err); + } + + assertion = utils.parseSamlAssertion(assertion); + + // copy all ancestor namespaces see https://github.com/auth0/xml-crypto/blob/d36a1bc0af40a5a3eec9c0c7b6b3f87bb0a0bca1/lib/signed-xml.js#L390-L392 + // When we extract the assertion for later usage, this assertion wont include all name spaces. All namespaces from parents + // nodes are used to calculate the digest. + collectAncestorNamespaces(assertion) + .filter((attr) => !assertion.getAttribute(attr.key)) + .forEach((attr) => assertion.setAttribute(attr.key, attr.value)); + + callback(null, assertion); + } + + var foundAssertions = xpath.select("//*[local-name(.)='Assertion']", samlpResponse); + if (foundAssertions.length > 1) { + return done(new Error('A SAMLResponse can contain only one Assertion element.')); + } + + // After being sure no more "Assertion" elements are found, we extract it from the expected place + var assertions = xpath.select("/*[local-name(.)='Response']/*[local-name(.)='Assertion' and namespace-uri(.)='" + saml2Namespace + "']", samlpResponse); + var token = assertions[0]; + + if (!token) { + // check for encrypted assertion + var encryptedAssertionPath = "/*[local-name(.)='Response']/*[local-name(.)='EncryptedAssertion' and namespace-uri(.)='" + saml2Namespace + "']"; + var encryptedAssertion = xpath.select(encryptedAssertionPath, samlpResponse); + if (encryptedAssertion.length > 1) { + return done(new Error('A SAMLResponse can contain only one EncryptedAssertion element.')); + } + + var encryptedToken = encryptedAssertion[0]; + if (encryptedToken) { + var encryptedData = encryptedToken.getElementsByTagNameNS('http://www.w3.org/2001/04/xmlenc#', 'EncryptedData')[0]; + if (!encryptedData) { + return done(new Error('EncryptedData not found.')); + } + + if (!this.options.decryptionKey) { + return done(new Error('Assertion is encrypted. Please set options.decryptionKey with your decryption private key.')); + } + + return xmlenc.decrypt(encryptedData, { + key: this.options.decryptionKey, + autopadding: this.options.autopadding, + disallowDecryptionWithInsecureAlgorithm: false, + warnInsecureAlgorithm: false + }, done); + } + } + + done(null, token); + }, + + getSamlStatus: function (samlResponse) { + var status = {}; + + samlResponse = utils.parseSamlResponse(samlResponse); + + // status code + var statusCodeXml = xpath.select("//*[local-name(.)='Status']/*[local-name(.)='StatusCode']", samlResponse)[0]; + if (statusCodeXml) { + status.code = statusCodeXml.getAttribute('Value'); + // status sub code + var statusSubCodeXml = xpath.select("//*[local-name(.)='Status']/*[local-name(.)='StatusCode']/*[local-name(.)='StatusCode']", samlResponse)[0]; + if (statusSubCodeXml) { + status.subCode = statusSubCodeXml.getAttribute('Value'); + } + } + + // status message + var samlStatusMsgXml = xpath.select("//*[local-name(.)='Status']/*[local-name(.)='StatusMessage']", samlResponse)[0]; + if (samlStatusMsgXml) { + status.message = samlStatusMsgXml.textContent; + } + + // status detail + var samlStatusDetailXml = xpath.select("//*[local-name(.)='Status']/*[local-name(.)='StatusDetail']", samlResponse)[0]; + if (samlStatusDetailXml) { + status.detail = samlStatusDetailXml.textContent; + } + + return status; + }, + + validateSamlResponse: function (samlResponse, callback) { + var self = this; + + samlResponse = utils.parseSamlResponse(samlResponse); + + // Check that the saml Resopnse actually has a Response object + var responseXMLs = xpath.select("//*[local-name(.)='Response']", samlResponse); + if (responseXMLs.length === 0) { + return callback(new Error('XML is not a valid saml response')); + } + if (responseXMLs.length > 1) { + return callback(new Error('SAMLResponse should be unique')); + } + var responseXML = responseXMLs[0]; + + self.isValidResponseID(responseXML.getAttribute('ID'), function(err){ + if (err && self.options.checkResponseID) { return callback(err); } + + var inResponseTo = responseXML.getAttribute('InResponseTo'); + + self.isValidInResponseTo(inResponseTo, function(err){ + if (err && self.options.checkInResponseTo) { return callback(err); } + + var destination = responseXML.getAttribute('Destination'); + + // https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf + // Page 36 of 91 + // Destination [Optional] + // A URI reference indicating the address to which this request has been sent. This is useful to prevent + // malicious forwarding of requests to unintended recipients, a protection that is required by some + // protocol bindings. If it is present, the actual recipient MUST check that the URI reference identifies the + // location at which the message was received. If it does not, the request MUST be discarded. Some + // protocol bindings may require the use of this attribute (see [SAMLBind]). + if (destination && destination !== self.options.destinationUrl){ + self.eventEmitter.emit('destinationValidationFailed', { + configuredDestination: self.options.destinationUrl, + assertionDestination: destination + }); + + if (self.options.checkDestination){ + return callback(new Error('Destination endpoint ' + destination + ' did not match ' + self.options.destinationUrl)); + } + } + + // check status + var samlStatus = self.getSamlStatus(samlResponse); + + // Check if this is a known error + var errorMessage = ErrorMessages[samlStatus.subCode] || + ErrorMessages[samlStatus.code]; + + if (errorMessage) { + // Return auth failed with the actual message or a friendly message + return callback (new AuthenticationFailedError(samlStatus.message || errorMessage, samlStatus.detail)); + } + + // extract assertion + self.extractAssertion(samlResponse, function (err, assertion) { + if (err) { return callback(err); } + if (!assertion) { + return callback(new Error('saml response does not contain an Assertion element (Status: ' + samlStatus.code + ')')); + } + + var samlResponseSignaturePath = "/*[local-name(.)='Response']/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']"; + var isResponseSigned = xpath.select(samlResponseSignaturePath, samlResponse).length > 0; + var samlAssertionSignaturePath = ".//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']"; + var isAssertionSigned = xpath.select(samlAssertionSignaturePath, assertion).length > 0; + + self.eventEmitter.emit('SAMLResponse:signatures', { + isResponseSigned: isResponseSigned, + isAssertionSigned: isAssertionSigned + }); + + if (!isResponseSigned && !isAssertionSigned) { + return callback(new Error('neither the response nor the assertion are signed')); + } + + if (isAssertionSigned) { + var assertionSignature = xpath.select(samlAssertionSignaturePath, assertion)[0]; + if (assertionSignature.prefix) { + try { + var dsigNamespace = assertionSignature.lookupNamespaceURI(assertionSignature.prefix); + if (dsigNamespace && !assertionSignature.getAttribute('xmlns:' + assertionSignature.prefix)) { + // saml assertion signature has a prefix but namespace is defined on parent, copy it to assertion + assertionSignature.setAttribute('xmlns:' + assertionSignature.prefix, dsigNamespace); + } + } catch(e) {} + } + + // If we find that a namespace was defined in resopnse and is used in assertion, we copy it to the assertion element + if (responseXML.attributes) { + var length = responseXML.attributes.length; + for (var i = 0; i < length; ++i) { + var attr = responseXML.attributes[i]; + // If attribute is a namespace, and is the signature prefix and is used in Assertion, cpy it to assertion + if (attr.name.indexOf("xmlns") === 0 && + attr.name.indexOf('xmlns:' + assertionSignature.prefix) === -1 && + xpath.select("//*[local-name(.)='Assertion']//*[namespace-uri(.)='" + attr.value + "'] or //*[local-name(.)='Assertion']//@*[namespace-uri(.)='" + attr.value + "']", samlResponse)) { + assertion.setAttribute(attr.name, attr.value); + } + } + } + } + + if (isResponseSigned) { + self._saml.validateSignature(samlResponse, { + cert: self.options.cert, + thumbprints: self.options.thumbprints, + signaturePath: samlResponseSignaturePath + }, + function (err) { + if (err) { return callback(err); } + + if (!isAssertionSigned) { + return self._saml.parseAssertion(assertion, callback); + } + + return self._saml.validateSamlAssertion(assertion, callback); + }); + } + else if (isAssertionSigned) { + return self._saml.validateSamlAssertion(assertion, callback); + } + }); + }); + }); + } +}; + +function generateInstant() { + var date = new Date(); + return date.getUTCFullYear() + '-' + ('0' + (date.getUTCMonth()+1)).slice(-2) + '-' + ('0' + date.getUTCDate()).slice(-2) + 'T' + ('0' + date.getUTCHours()).slice(-2) + ":" + ('0' + date.getUTCMinutes()).slice(-2) + ":" + ('0' + date.getUTCSeconds()).slice(-2) + "Z"; +} diff --git a/lib/passport-wsfed-saml2/state/null.js b/lib/passport-wsfed-saml2/state/null.js new file mode 100644 index 0000000..bf8cc75 --- /dev/null +++ b/lib/passport-wsfed-saml2/state/null.js @@ -0,0 +1,12 @@ +function NullStore(options) { +} + +NullStore.prototype.store = function(req, cb) { + cb(); +}; + +NullStore.prototype.verify = function(req, providedState, cb) { + cb(null, true); +}; + +module.exports = NullStore; diff --git a/lib/passport-wsfed-saml2/state/session.js b/lib/passport-wsfed-saml2/state/session.js new file mode 100644 index 0000000..33319e0 --- /dev/null +++ b/lib/passport-wsfed-saml2/state/session.js @@ -0,0 +1,85 @@ +var uid = require('uid2'); + +/** + * Creates an instance of `SessionStore`. + * + * This is the state store implementation used when + * the `state` option is enabled. It generates a random state and stores it in + * `req.session` and verifies it when the service provider redirects the user + * back to the application. + * + * This state store requires session support. If no session exists, an error + * will be thrown. + * + * Options: + * + * - `key` The key in the session under which to store the state + * + * @constructor + * @param {Object} options + * @api public + */ +function SessionStore(options) { + if (!options.key) { throw new TypeError('Session-based state store requires a session key'); } + this._key = options.key; +} + +/** + * Store request state. + * + * This implementation simply generates a random string and stores the value in + * the session, where it will be used for verification when the user is + * redirected back to the application. + * + * @param {Object} req + * @param {Function} callback + * @api protected + */ +SessionStore.prototype.store = function(req, callback) { + if (!req.session) { return callback(new Error('Authentication requires session support when using state. Did you forget to use express-session middleware?')); } + + var key = this._key; + var state = uid(24); + if (!req.session[key]) { req.session[key] = {}; } + req.session[key].state = state; + callback(null, state); +}; + +/** + * Verify request state. + * + * This implementation simply compares the state parameter in the request to the + * value generated earlier and stored in the session. + * + * @param {Object} req + * @param {String} providedState + * @param {Function} callback + * @api protected + */ +SessionStore.prototype.verify = function(req, providedState, callback) { + if (!req.session) { return callback(new Error('Authentication requires session support when using state. Did you forget to use express-session middleware?')); } + + var key = this._key; + if (!req.session[key]) { + return callback(null, false, { message: 'Unable to verify authorization request state.' }); + } + + var state = req.session[key].state; + if (!state) { + return callback(null, false, { message: 'Unable to verify authorization request state.' }); + } + + delete req.session[key].state; + if (Object.keys(req.session[key]).length === 0) { + delete req.session[key]; + } + + if (state !== providedState) { + return callback(null, false, { message: 'Invalid authorization request state.' }); + } + + return callback(null, true); +}; + +// Expose constructor. +module.exports = SessionStore; diff --git a/lib/passport-wsfed-saml2/strategy.js b/lib/passport-wsfed-saml2/strategy.js new file mode 100644 index 0000000..31853ef --- /dev/null +++ b/lib/passport-wsfed-saml2/strategy.js @@ -0,0 +1,300 @@ +var util = require('util'); +var url = require('url'); +var jwt = require('jsonwebtoken'); +var Strategy = require('passport-strategy'); +var saml = require('./saml'); +var wsfed = require('./wsfederation'); +var samlp = require('./samlp'); +var getReqUrl = require('./utils').getReqUrl; +var EventEmitter = require('events'); +var utils = require('./utils'); + +var utils = require('./utils'); +var NullStateStore = require('./state/null'); +var SessionStateStore = require('./state/session'); + +function WsFedSaml2Strategy (options, verify) { + if (typeof options === 'function') { + verify = options; + options = {}; + } + + this.options = options || {}; + this.options.protocol = this.options.protocol || 'wsfed'; + this.options.eventEmitter = this.options.eventEmitter || new EventEmitter(); + + if (!verify) { + throw new Error('this strategy requires a verify function'); + } + + this.name = 'wsfed-saml2'; + + Strategy.call(this); + + this._verify = verify; + this._passReqToCallback = !!options.passReqToCallback; + + if (!this.options.jwt) { + this._saml = new saml.SAML(this.options); + this._samlp = new samlp(this.options, this._saml); + } else { + this._jwt = this.options.jwt; + } + + this._wsfed = new wsfed(options.realm, options.homeRealm, options.identityProviderUrl, options.wreply); + + this._key = options.sessionKey || (this.options.protocol + ':' + url.parse(options.identityProviderUrl || '').hostname); + + if (options.store) { + this._stateStore = options.store; + } else { + if (options.state) { + this._stateStore = new SessionStateStore({ key: this._key }); + } else { + this._stateStore = new NullStateStore(); + } + } + + this.events = this.options.eventEmitter; +} + +util.inherits(WsFedSaml2Strategy, Strategy); + +WsFedSaml2Strategy.prototype._authenticate_saml = function (req, state) { + var self = this; + + self._wsfed.retrieveToken(req, function(err, token) { + if (err) return self.fail(err, err.status || 400); + + self.options.recipientUrl = self.options.recipientUrl || getReqUrl(req); + + self._saml.validateSamlAssertion(token, function (err, profile) { + if (err) { + return self.error(err); + } + + var verified = function (err, user, info) { + if (err) { + return self.error(err); + } + + if (!user) { + return self.fail(info); + } + + info = info || {}; + if (state) { info.state = state; } + self.success(user, info); + }; + + if (self._passReqToCallback) { + self._verify(req, profile, verified); + } else { + self._verify(profile, verified); + } + }) + }); + +}; + +WsFedSaml2Strategy.prototype._authenticate_jwt = function (req, state) { + var self = this; + var token = req.body.wresult; + + jwt.verify(token, this.options.cert, this._jwt, function (err, profile) { + if (err) { + return self.error(err); + } + + var verified = function (err, user, info) { + if (err) { + return self.error(err); + } + + if (!user) { + return self.fail(info); + } + + info = info || {}; + if (state) { info.state = state; } + self.success(user, info); + }; + + if (self._passReqToCallback) { + self._verify(req, profile, verified); + } else { + self._verify(profile, verified); + } + }); +}; + +WsFedSaml2Strategy.prototype.authenticate = function (req, opts) { + var self = this; + var protocol = opts.protocol || this.options.protocol; + + var meta = { + identityProviderUrl: this.options.identityProviderUrl + }; + + var storeState = function (stored) { + try { + var arity = self._stateStore.store.length; + if (arity === 3) { + self._stateStore.store(req, meta, stored); + } else { // arity == 2 + self._stateStore.store(req, stored); + } + } catch (ex) { + return self.error(ex); + } + }; + + var verifyState = function (state, loaded) { + try { + var arity = self._stateStore.verify.length; + if (arity === 4) { + self._stateStore.verify(req, state, meta, loaded); + } else { // arity == 3 + self._stateStore.verify(req, state, loaded); + } + } catch (ex) { + return self.error(ex); + } + }; + + function executeWsfed(req) { + if (req.body && req.method === 'POST' && req.body.wresult) { + // We have a response, get the user identity out of it + var loaded = function (err, ok, state) { + if (err) { return self.error(err); } + if (!ok) { return self.fail(state, 403); } + + if (self._jwt) { + self._authenticate_jwt(req, state); + } else { + self._authenticate_saml(req, state); + } + }; + + verifyState(req.body.wctx, loaded); + } else { + // Initiate new ws-fed authentication request + var authzParams = self.authorizationParams(opts); + var redirectToIdp = function () { + var idpUrl = self._wsfed.getRequestSecurityTokenUrl(authzParams); + self.redirect(idpUrl); + }; + + var state = opts.wctx; + if (state) { + authzParams.wctx = state; + redirectToIdp(); + } else { + var stored = function (err, state) { + if (err) { return self.error(err); } + if (state) { authzParams.wctx = state; } + redirectToIdp(); + }; + + storeState(stored); + } + } + } + + function executeSamlp(req) { + if (req.body && req.method === 'POST' && req.body.SAMLResponse) { + // We have a response, get the user identity out of it + var loaded = function (err, ok, state) { + if (err) { return self.error(err); } + if (!ok) { return self.fail(state, 403); } + + var samlResponse = self._samlp.decodeResponse(req); + if (samlResponse.indexOf('<') === -1) { + return self.fail('SAMLResponse should be a valid xml', 400); + } + + var samlResponseDom = utils.parseSamlResponse(samlResponse); + + // If options are not set, we set the expected value from the request object + var req_full_url = getReqUrl(req); + self.options.destinationUrl = self.options.destinationUrl || req_full_url; + self.options.recipientUrl = self.options.recipientUrl || req_full_url; + + + self._samlp.validateSamlResponse(samlResponseDom, function (err, profile) { + if (err) return self.fail(err, err.status || 400); + + var verified = function (err, user, info) { + if (err) return self.error(err); + if (!user) return self.fail(info); + + info = info || {}; + if (state) { info.state = state; } + self.success(user, info); + }; + + if (self._passReqToCallback) { + self._verify(req, profile, verified); + } else { + self._verify(profile, verified); + } + }); + }; + + verifyState(req.body.RelayState, loaded); + } else { + // Initiate new samlp authentication request + var authzParams = self.authorizationParams(opts); + authzParams.request_id = '_' + utils.generateUniqueID() + meta.saml_request_id = authzParams.request_id; + + var sendRequestToIdp = function () { + if (self.options.protocolBinding === 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST') { + self._samlp.getSamlRequestForm(authzParams, function (err, form) { + if (err) return self.error(err); + var res = req.res; + res.set('Content-Type', 'text/html'); + res.send(form); + }); + } + else { + self._samlp.getSamlRequestUrl(authzParams, function (err, url) { + if (err) return self.error(err); + self.redirect(url); + }); + } + }; + + var state = opts.RelayState; + if (state) { + authzParams.RelayState = state; + sendRequestToIdp(); + } else { + var stored = function (err, state) { + if (err) { return self.error(err); } + if (state) { authzParams.RelayState = state; } + sendRequestToIdp(); + }; + + storeState(stored); + } + } + } + + switch (protocol) { + case 'wsfed': + executeWsfed(req, this.options); + break; + case 'samlp': + executeSamlp(req, this.options); + break; + default: + throw new Error('not supported protocol: ' + protocol); + } +}; + +WsFedSaml2Strategy.prototype.authorizationParams = function(options) { + return options; +}; + +module.exports = WsFedSaml2Strategy; diff --git a/lib/passport-wsfed-saml2/templates.js b/lib/passport-wsfed-saml2/templates.js new file mode 100644 index 0000000..039a528 --- /dev/null +++ b/lib/passport-wsfed-saml2/templates.js @@ -0,0 +1,11 @@ +var ejs = require('ejs'); +var fs = require('fs'); +var path = require('path'); + +var templates = fs.readdirSync(path.join(__dirname, 'templates')); + +templates.forEach(function (tmplFile) { + var content = fs.readFileSync(path.join(__dirname, 'templates', tmplFile)); + var template = ejs.compile(content.toString()); + exports[tmplFile.slice(0, -4)] = template; +}); \ No newline at end of file diff --git a/lib/passport-wsfed-saml2/templates/assert_and_destination.ejs b/lib/passport-wsfed-saml2/templates/assert_and_destination.ejs new file mode 100644 index 0000000..bf1eef0 --- /dev/null +++ b/lib/passport-wsfed-saml2/templates/assert_and_destination.ejs @@ -0,0 +1,6 @@ +<% if (AssertionConsumerServiceURL) { -%> + AssertionConsumerServiceURL="<%= AssertionConsumerServiceURL %>" +<% } -%> +<% if (Destination) { -%> + Destination="<%= Destination %>" +<% } -%> \ No newline at end of file diff --git a/lib/passport-wsfed-saml2/templates/form.ejs b/lib/passport-wsfed-saml2/templates/form.ejs new file mode 100644 index 0000000..7f3d429 --- /dev/null +++ b/lib/passport-wsfed-saml2/templates/form.ejs @@ -0,0 +1,21 @@ + + + Working... + + +
+ + + +
+ + + \ No newline at end of file diff --git a/lib/passport-wsfed-saml2/templates/samlrequest.ejs b/lib/passport-wsfed-saml2/templates/samlrequest.ejs new file mode 100644 index 0000000..9e3616f --- /dev/null +++ b/lib/passport-wsfed-saml2/templates/samlrequest.ejs @@ -0,0 +1,7 @@ + + ID="<%= ID %>" + IssueInstant="<%= IssueInstant %>"<% if (ForceAuthn) { %> ForceAuthn="true"<% } %> + ProtocolBinding="<%= ProtocolBinding %>" Version="2.0"> + <%= Issuer %> + \ No newline at end of file diff --git a/lib/passport-wsfed-saml2/utils.js b/lib/passport-wsfed-saml2/utils.js new file mode 100644 index 0000000..c250f88 --- /dev/null +++ b/lib/passport-wsfed-saml2/utils.js @@ -0,0 +1,113 @@ +var xmldom = require('@auth0/xmldom'); +var crypto = require('crypto'); + +var SamlAssertionParserError = require('./errors/SamlAssertionParserError'); +var SamlResponseParserError = require('./errors/SamlResponseParserError'); +var WSFederationResultParserError = require('./errors/WSFederationResultParserError'); + +const CERT_START = "-----BEGIN CERTIFICATE-----\n"; +const CERT_END = "\n-----END CERTIFICATE-----\n"; + +exports.certToPEM = (cert) => CERT_START + cert.match(/.{1,64}/g).join('\n') + CERT_END; + +// convert from \r\n -> \n this should be done by the xml parser, but is ignoring this. +function crlf2lf(string) { + return string.replace(/\r\n?/g, '\n'); +} + +exports.getSamlAssertionVersion = function(samlAssertion){ + if (samlAssertion.getAttribute('MajorVersion') === '1') { + return '1.1'; + } else if (samlAssertion.getAttribute('Version') === '2.0'){ + return '2.0'; + } else { + // In this case the version is undefined, or we weren't able to determine it. + return undefined; + } + +}; + +exports.parseSamlAssertion = function(xml) { + if (typeof xml === 'string') { + try { + return new xmldom.DOMParser().parseFromString(crlf2lf(xml)); + } catch (e) { + throw new SamlAssertionParserError('SAML Assertion should be a valid xml', e); + } + } + + return xml; +}; + +exports.parseSamlResponse = function(xml) { + if (typeof xml === 'string') { + try { + return new xmldom.DOMParser().parseFromString(crlf2lf(xml)); + } catch (e) { + throw new SamlResponseParserError('SAMLResponse should be a valid xml', e); + } + } + + return xml; +}; + +exports.parseWsFedResponse = function(xml) { + if (typeof xml === 'string') { + try { + return new xmldom.DOMParser().parseFromString(crlf2lf(xml)); + } catch (e) { + throw new WSFederationResultParserError('wresult should be a valid xml', e); + } + } + + return xml; +}; + +exports.getReqUrl = function(req){ + return req.protocol + '://' + (req.get('x-forwarded-host') || req.get('host')) + req.originalUrl; +}; + +exports.generateUniqueID = function() { + var uniqueID = crypto.randomBytes(16); + return uniqueID.toString('hex'); +}; + +exports.getEncoding = function(xml){ + try{ + const response = new xmldom.DOMParser().parseFromString(crlf2lf(xml)); + // -> read encoding + if (response.firstChild && response.firstChild.tagName == 'xml'){ + const regex = /(?:encoding=\")([^\"]*)(?:\")/g; + const match = regex.exec(response.firstChild.nodeValue); + // [0] the complete match + // [1] the specific encoding + if (match && match.length >= 2){ + // encoding value + return match[1]; + } + } + } catch(e){ + return; + } +}; + +/** + * Safely compare two string. Type validation and length comparison are inspired in the + * cryptiles.fixedTimeComparison method and kept to avoid the linear validation when + * comparing the two strings at the end of the method. + * + * @param a + * @param b + * @return {boolean} + */ +exports.stringCompare = function(a,b) { + if (typeof a !== 'string' || typeof b !== 'string') { + return false; + } + + if (a.length !== b.length) { + return false; + } + + return a === b; +}; diff --git a/lib/passport-wsfed-saml2/wsfederation.js b/lib/passport-wsfed-saml2/wsfederation.js new file mode 100644 index 0000000..f7b3b70 --- /dev/null +++ b/lib/passport-wsfed-saml2/wsfederation.js @@ -0,0 +1,114 @@ +var xtend = require('xtend'); +var qs = require('querystring'); +var xpath = require('xpath'); + +var utils = require('./utils'); +var AuthenticationFailedError = require('./errors/AuthenticationFailedError'); + +var WsFederation = module.exports = function WsFederation (realm, homerealm, identityProviderUrl, wreply) { + this.realm = realm; + this.homerealm = homerealm; + this.identityProviderUrl = identityProviderUrl; + this.wreply = wreply; +}; + +WsFederation.prototype = { + getRequestSecurityTokenUrl: function (options) { + var query = xtend(options || {}, { + wtrealm: this.realm, + wa: 'wsignin1.0' + }); + + if (this.homerealm !== '') { + query.whr = this.homerealm; + } + + if (this.wreply) { + query.wreply = this.wreply; + } + + return this.identityProviderUrl + '?' + qs.encode(query); + }, + + extractToken: function(req) { + var doc = utils.parseWsFedResponse(req.body['wresult']); + + // //Probe WS-Trust 1.2 namespace (http://schemas.xmlsoap.org/ws/2005/02/trust) + var token = doc.getElementsByTagNameNS('http://schemas.xmlsoap.org/ws/2005/02/trust', 'RequestedSecurityToken')[0]; + + // //Probe WS-Trust 1.3 namespace (http://docs.oasis-open.org/ws-sx/ws-trust/200512) + if(!token){ + token = doc.getElementsByTagNameNS('http://docs.oasis-open.org/ws-sx/ws-trust/200512', 'RequestedSecurityToken')[0]; + } + + return token && token.firstChild; + }, + + retrieveToken: function(req, callback) { + if (req.body.wresult.indexOf('<') === -1) { + return callback(new Error('wresult should be a valid xml')); + } + + var fault = this.extractFault(req); + if (fault) { + return callback(new AuthenticationFailedError(fault.message, fault.detail)); + } + + var token = this.extractToken(req); + if (!token) { + return callback(new Error('missing RequestedSecurityToken element')); + } + + callback(null, token); + }, + + extractFault: function(req) { + var fault = {}; + var doc = utils.parseWsFedResponse(req.body['wresult']); + + var isFault = xpath.select("//*[local-name(.)='Fault']", doc)[0]; + if (!isFault) { + return null; + } + + var codeXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Code']/*[local-name(.)='Value']", doc)[0]; + if (codeXml) { + fault.code = codeXml.textContent; + } + + var subCodeXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Code']/*[local-name(.)='Subcode']/*[local-name(.)='Value']", doc)[0]; + if (subCodeXml) { + fault.subCode = subCodeXml.textContent; + } + + var messageXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Reason']/*[local-name(.)='Text']", doc)[0]; + if (messageXml) { + fault.message = messageXml.textContent; + } + + var detailXml = xpath.select("//*[local-name(.)='Fault']/*[local-name(.)='Detail']", doc)[0]; + if (detailXml) { + fault.detail = detailXml.textContent; + } + + return fault; + } +}; + +Object.defineProperty(WsFederation, 'realm', { + get: function () { + return this.realm; + } +}); + +Object.defineProperty(WsFederation, 'homeRealm', { + get: function () { + return this.homeRealm; + } +}); + +Object.defineProperty(WsFederation, 'identityProviderUrl', { + get: function () { + return this.identityProviderUrl; + } +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..8457350 --- /dev/null +++ b/package.json @@ -0,0 +1,76 @@ +{ + "name": "@labshare/passport-wsfed-saml2", + "version": "4.6.1", + "description": "SAML2 Protocol and WS-Fed library", + "scripts": { + "test": "./node_modules/.bin/mocha --recursive", + "test-debug": "./node_modules/.bin/mocha --recursive --inspect-brk=8217", + "cover": "nyc npm run test" + }, + "author": { + "name": "Matias Woloski", + "email": "matias@auth0.com", + "url": "http://auth0.com/" + }, + "repository": { + "type": "git", + "url": "https://github.com/LabShare/passport-wsfed-saml2.git" + }, + "bugs": { + "url": "http://github.com/LabShare/passport-wsfed-saml2/issues" + }, + "main": "./lib/passport-wsfed-saml2", + "dependencies": { + "@auth0/xmldom": "0.1.22", + "ejs": "2.5.5", + "jsonwebtoken": "~5.0.4", + "node-forge": "^0.10.0", + "passport-strategy": "^1.0.0", + "uid2": "0.0.x", + "valid-url": "^1.0.9", + "xml-crypto": "^2.1.2", + "xml-encryption": "^1.2.1", + "xpath": "0.0.5", + "xtend": "~2.0.3" + }, + "devDependencies": { + "@labshare/semantic-release-config": "^1.1.4", + "chai": "~4.3.4", + "chai-passport-strategy": "1.x.x", + "cheerio": "~1.0.0-rc.10", + "express": "~3.11.0", + "mocha": "~9.0.2", + "nyc": "~15.1.0", + "passport": "^0.4.1", + "request": "~2.88.0", + "saml": "~0.4.4", + "samlp": "~0.4.3", + "semantic-release": "^15.14.0", + "wsfed": "~0.3.5" + }, + "engines": { + "node": ">= 12" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://www.opensource.org/licenses/MIT" + } + ], + "release": { + "extends": "@labshare/semantic-release-config" + }, + "keywords": [ + "labshare", + "saml", + "wsfed", + "passport", + "auth0", + "azure", + "auth", + "authn", + "authentication", + "identity", + "adfs" + ] +} diff --git a/test/fixture/samlp-server.js b/test/fixture/samlp-server.js new file mode 100644 index 0000000..c8fc337 --- /dev/null +++ b/test/fixture/samlp-server.js @@ -0,0 +1,432 @@ +var express = require('express'); +var http = require('http'); +var samlp = require('samlp'); +var xtend = require('xtend'); +var fs = require('fs'); +var path = require('path'); + +var passport = require('passport'); +var Strategy = require('../../lib/passport-wsfed-saml2').Strategy; + +var identityProviderUrl = 'http://localhost:5051/samlp'; +var relayState = 'somestate'; + +passport.use('samlp', new Strategy({ + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + recipientUrl: 'https://auth0-dev-ed.my.salesforce.com', + destinationUrl: 'https://auth0-dev-ed.my.salesforce.com' + }, function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-http-post', new Strategy({ + protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'] + }, function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-custom-request-template', new Strategy({ + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + requestTemplate: '', + requestContext: { + Foo: { + Test: 123 + } + } + }, function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-idpurl-with-querystring', new Strategy( + { + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl + '?foo=bar', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'] + }, + function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-signedrequest-without-deflate', new Strategy({ + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + signingKey: { + key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), + cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), + }, + deflate: false + }, function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-signedrequest-with-deflate', new Strategy({ + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + signingKey: { + key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), + cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), + }, + deflate: true + }, function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-signedrequest-post', new Strategy({ + protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + signingKey: { + key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), + cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), + }, + deflate: false + }, function(profile, done) { + return done(null, profile); + }) +); + + +passport.use('samlp-signedresponse', new Strategy( + { + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: identityProviderUrl, + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + recipientUrl: 'https://auth0-dev-ed.my.salesforce.com', + destinationUrl: 'https://auth0-dev-ed.my.salesforce.com' + }, + function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-signedresponse-invalidcert', new Strategy( + { + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: identityProviderUrl, + thumbprints: ['11111111111111111a5b93a43572eb2376fed309'], + recipientUrl: 'https://auth0-dev-ed.my.salesforce.com', + destinationUrl: 'https://auth0-dev-ed.my.salesforce.com' + }, + function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-invalidcert', new Strategy( + { + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: identityProviderUrl, + thumbprints: ['11111111111111111a5b93a43572eb2376fed309'] + }, + function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-signedresponse-signedassertion', new Strategy( + { + path: '/callback', + realm: 'urn:auth0:login-dev3', + thumbprints: ['C9ED4DFB07CAF13FC21E0FEC1572047EB8A7A4CB'], + destinationUrl: 'https://login-dev3.auth0.com:3000/login/callback', + recipientUrl: 'https://login-dev3.auth0.com:3000/login/callback', + checkExpiration: false // we are using a precomputed assertion generated from a sample idp feide + }, + function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-ping', new Strategy( + { + path: '/callback', + realm: 'urn:auth0:login-dev3', + thumbprints: ['44340220770a348444be34970939cff8a2d74f08'], + destinationUrl: 'https://login-dev3.auth0.com:3000/login/callback', + recipientUrl: 'https://login-dev3.auth0.com:3000/login/callback', + checkExpiration: false // we are using a precomputed assertion generated from a sample idp feide + }, + function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-okta', new Strategy( + { + path: '/callback', + realm: 'https://auth0145.auth0.com', + thumbprints: ['a0c7dbb790e3476d3c5dd236f9f2060b1fd6e253'], + destinationUrl: 'https://auth0145.auth0.com', + recipientUrl: 'https://auth0145.auth0.com', + checkExpiration: false // we are using a precomputed assertion generated from a sample idp feide + }, + function(profile, done) { + return done(null, profile); + }) +); + +function pemToCert(pem) { + // if certificate doesn't have ---- begin cert --- just return the pem + if (!/-----BEGIN CERTIFICATE-----/.test(pem.toString())) { + return pem.toString(); + } + + var cert = /-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----/g.exec(pem.toString()); + if (cert.length > 0) { + return cert[1].replace(/[\n|\r\n]/g, ''); + } + + return null; +} + +passport.use('samlp-with-utf8', new Strategy( + { + path: '/callback', + thumbprints: ['119B9E027959CDB7C662CFD075D9E2EF384E445F'], + decryptionKey: fs.readFileSync(path.join(__dirname, '../test-auth0.key')), + recipientUrl: 'https://login0.myauth0.com/login/callback', + destinationUrl: 'https://login0.myauth0.com/login/callback', + checkExpiration: false, // we are using a precomputed assertion generated from a sample idp feide + checkSPNameQualifier: false, + checkAudience: false + }, + function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-with-ISO', new Strategy( + { + path: '/callback', + cert: pemToCert(fs.readFileSync(path.join(__dirname, '../test-auth0.pem'))), + checkExpiration: false, // we are using a precomputed assertion generated from a sample idp feide + checkAudience: false, + checkDestination: false, + checkRecipient: false + }, + function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-with-ISO-explicit', new Strategy( + { + path: '/callback', + cert: pemToCert(fs.readFileSync(path.join(__dirname, '../test-auth0.pem'))), + checkExpiration: false, // we are using a precomputed assertion generated from a sample idp feide + checkAudience: false, + default_encoding: 'ISO-8859-1', + checkDestination: false, + checkRecipient: false + }, + function(profile, done) { + return done(null, profile); + }) +); + +passport.use('samlp-with-dsig-at-root', new Strategy( + { + path: '/callback', + checkExpiration: false, // we are using a precomputed assertion generated from a sample idp + checkAudience: false, + checkDestination: false, + cert: pemToCert(fs.readFileSync(path.join(__dirname, '../test-auth0.pem'))) + }, + function(profile, done) { + return done(null, profile); + }) +); + + +var fakeUser = { + id: '12345678', + displayName: 'John Foo', + name: { + familyName: 'Foo', + givenName: 'John' + }, + emails: [ + { + type: 'work', + value: 'jfoo@gmail.com' + } + ] +}; + +var credentials = { + cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), + key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')) +}; + +module.exports.options = {}; + +module.exports.start = function(options, callback){ + module.exports.options = options; + if (typeof options === 'function') { + callback = options; + module.exports.options = {}; + } + + var app = express(); + + app.configure(function(){ + this.use(express.bodyParser()); + this.use(passport.initialize()); + this.use(passport.session()); + this.use(function(req,res,next){ + req.user = fakeUser; + next(); + }); + }); + + function getPostURL (audience, samlRequestDom, req, callback) { + callback(null, 'http://localhost:5051/callback'); + } + + //configure samlp middleware + app.get('/samlp', function(req, res, next) { + samlp.auth(xtend({}, { + issuer: 'urn:fixture-test', + getPostURL: getPostURL, + cert: credentials.cert, + key: credentials.key, + recipient: 'https://auth0-dev-ed.my.salesforce.com' + }, module.exports.options))(req, res); + }); + + app.get('/login', passport.authenticate('samlp', { protocol: 'samlp', RelayState: relayState })); + app.get('/login-http-post', passport.authenticate('samlp-http-post', { protocol: 'samlp', RelayState: relayState })); + app.get('/login-idp-with-querystring', passport.authenticate('samlp-idpurl-with-querystring', { protocol: 'samlp', RelayState: relayState })); + + app.get('/login-signed-request-without-deflate', passport.authenticate('samlp-signedrequest-without-deflate', { protocol: 'samlp', RelayState: relayState })); + app.get('/login-signed-request-post', passport.authenticate('samlp-signedrequest-post', { protocol: 'samlp', RelayState: relayState })); + app.get('/login-signed-request-with-deflate', passport.authenticate('samlp-signedrequest-with-deflate', { protocol: 'samlp', RelayState: relayState })); + + app.get('/login-custom-request-template', + passport.authenticate('samlp-custom-request-template', { protocol: 'samlp', RelayState: relayState })); + + app.post('/callback', + function(req, res, next) { + next(); + }, + passport.authenticate('samlp', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-signedresponse', + passport.authenticate('samlp-signedresponse', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-signedresponse-invalidcert', + passport.authenticate('samlp-signedresponse-invalidcert', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-invalidcert', + passport.authenticate('samlp-invalidcert', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-signedresponse-signedassertion', + passport.authenticate('samlp-signedresponse-signedassertion', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-ping', + passport.authenticate('samlp-ping', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-okta', + passport.authenticate('samlp-okta', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-with-utf8', + passport.authenticate('samlp-with-utf8', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-with-ISO', + passport.authenticate('samlp-with-ISO', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-with-ISO-explicit', + passport.authenticate('samlp-with-ISO-explicit', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } +); + + app.post('/callback/samlp-with-invalid-xml', + function (req, res, next) { + passport.authenticate('samlp-with-utf8', { protocol: 'samlp' }, function(err, user, info) { + res.send(400, { message: err.message }); + })(req, res, next); + }, + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback/samlp-with-dsig-at-root', + passport.authenticate('samlp-with-dsig-at-root', { protocol: 'samlp' }), + function(req, res) { + res.json(req.user); + } + ); + + var server = http.createServer(app).listen(5051, callback); + module.exports.close = server.close.bind(server); +}; + +module.exports.relayState = relayState; +module.exports.identityProviderUrl = identityProviderUrl; +module.exports.fakeUser = fakeUser; +module.exports.credentials = credentials; diff --git a/test/fixture/wsfed-server.js b/test/fixture/wsfed-server.js new file mode 100644 index 0000000..2a068ef --- /dev/null +++ b/test/fixture/wsfed-server.js @@ -0,0 +1,106 @@ +var express = require('express'); +var http = require('http'); +var wsfed = require('wsfed'); +var xtend = require('xtend'); +var fs = require('fs'); +var path = require('path'); + +var passport = require('passport'); +var Strategy = require('../../lib/passport-wsfed-saml2').Strategy; + +passport.use(new Strategy( + { + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://localhost:5050/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'] + }, + function(profile, done) { + return done(null, profile); + }) +); + +var fakeUser = { + id: '12345678', + displayName: 'John Foo', + name: { + familyName: 'Foo', + givenName: 'John' + }, + emails: [ + { + type: 'work', + value: 'jfoo@gmail.com' + } + ] +}; + +var credentials = { + cert: fs.readFileSync(path.join(__dirname, '../test-auth0.pem')), + key: fs.readFileSync(path.join(__dirname, '../test-auth0.key')) +}; + +passport.serializeUser(function(user, done) { + done(null, user); +}); + +passport.deserializeUser(function(user, done) { + done(null, user); +}); + +module.exports.options = {}; + +module.exports.start = function(options, callback){ + module.exports.options = options; + if (typeof options === 'function') { + callback = options; + module.exports.options = {}; + } + + var app = express(); + + app.configure(function(){ + this.use(express.bodyParser()); + this.use(passport.initialize()); + this.use(passport.session()); + this.use(function(req,res,next){ + req.user = fakeUser; + next(); + }); + }); + + function getPostURL (wtrealm, wreply, req, callback) { + callback(null, 'http://localhost:5050/callback'); + } + + app.get('/login', + wsfed.auth(xtend({}, { + issuer: 'fixture-test', + getPostURL: getPostURL, + cert: credentials.cert, + key: credentials.key + }, options))); + + app.post('/callback/wresult-with-invalid-xml', + function (req, res, next) { + passport.authenticate('wsfed-saml2', function(err, user, info) { + res.send(400, { message: err.message }); + })(req, res, next); + }, + function(req, res) { + res.json(req.user); + } + ); + + app.post('/callback', + passport.authenticate('wsfed-saml2'), + function(req, res) { + res.json(req.user); + }); + + var server = http.createServer(app).listen(5050, callback); + module.exports.close = server.close.bind(server); +}; + +module.exports.fakeUser = fakeUser; +module.exports.credentials = credentials; diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 0000000..e7260b0 --- /dev/null +++ b/test/helpers.js @@ -0,0 +1,65 @@ +var xmlCrypto = require('xml-crypto'), + crypto = require('crypto'), + xmldom = require('@auth0/xmldom'); + +exports.isValidSignature = function(assertion, cert) { + var doc = new xmldom.DOMParser().parseFromString(assertion); + var signature = xmlCrypto.xpath(doc, "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0]; + var sig = new xmlCrypto.SignedXml(null, { idAttribute: 'AssertionID' }); + sig.keyInfoProvider = { + getKeyInfo: function (key) { + return ""; + }, + getKey: function (keyInfo) { + return cert; + } + }; + sig.loadSignature(signature.toString()); + return sig.checkSignature(assertion); +}; + +exports.getIssuer = function(assertion) { + var doc = new xmldom.DOMParser().parseFromString(assertion); + return doc.documentElement.getAttribute('Issuer'); +}; + +exports.getAssertionID = function(assertion) { + var doc = new xmldom.DOMParser().parseFromString(assertion); + return doc.documentElement.getAttribute('AssertionID'); +}; + +exports.getIssueInstant = function(assertion) { + var doc = new xmldom.DOMParser().parseFromString(assertion); + return doc.documentElement.getAttribute('IssueInstant'); +}; + +exports.getConditions = function(assertion) { + var doc = new xmldom.DOMParser().parseFromString(assertion); + return doc.documentElement.getElementsByTagName('saml:Conditions'); +}; + +exports.getAudiences = function(assertion) { + var doc = new xmldom.DOMParser().parseFromString(assertion); + return doc.documentElement + .getElementsByTagName('saml:Conditions')[0] + .getElementsByTagName('saml:AudienceRestrictionCondition')[0] + .getElementsByTagName('saml:Audience'); +}; + +exports.getAuthenticationStatement = function(assertion) { + var doc = new xmldom.DOMParser().parseFromString(assertion); + return doc.documentElement + .getElementsByTagName('saml:AuthenticationStatement')[0]; +}; + +exports.getAttributes = function(assertion) { + var doc = new xmldom.DOMParser().parseFromString(assertion); + return doc.documentElement + .getElementsByTagName('saml:Attribute'); +}; + +exports.getNameIdentifier = function(assertion) { + var doc = new xmldom.DOMParser().parseFromString(assertion); + return doc.documentElement + .getElementsByTagName('saml:NameIdentifier')[0]; +}; diff --git a/test/interop.tests.js b/test/interop.tests.js new file mode 100644 index 0000000..a7e0887 --- /dev/null +++ b/test/interop.tests.js @@ -0,0 +1,500 @@ +const assert = require('assert'); +const fs = require('fs'); +const SamlPassport = require('../lib/passport-wsfed-saml2/saml').SAML; +const samlp = require('../lib/passport-wsfed-saml2/samlp'); +const wsfed = require('../lib/passport-wsfed-saml2').Strategy; + +const request = require('request'); +const server = require('./fixture/samlp-server'); +const expect = require('chai').expect; + +describe('interop', function () { + + before(function (done) { + server.start(done); + }); + + after(function (done) { + server.close(done); + }); + + + it('should validate an assertion from office365', function (done) { + var signedAssertion = 'https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/TzJmLs0BTPgpaPLsA7L2Kd9l1k4IBOmwIM/znV2iOPU=OHJCAffCNPRkwsE3RqnVPoCRSqsPrio8prABauzu2pqF418Y1QJuJehhzztY8A6kwnBUkBVE7BIyLe7kgCnBoNZWElYki1xtaLksc/Afc0TjlZvv9IJ9fQHIBiL1JA9KcySq1tu9dv/NauykBODXuljPuVTk6I4xLLWcg20o26Ov57axp42uWPpcJHtasomLmmmnAXEh6P7aB/1Vlm/MAJhWXToxacauJzFao3F9JNEuucKY6y3RPDp1Qq3vL0gq98RKuiaejayu6RjyyU2+8vCBzURul8b7ZXPUHfIOME6Q5LvbKqLhe/mzqRc+9GUg22X3B5SYjdnXjwHbBTbihA==MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng10030000838D23AF@MicrosoftOnline.comspn:408153f4-5960-43dc-9d4f-6b717d772c8d75696069-df44-4310-9bcf-08b45e3007c9Matiasmatias@auth0.onmicrosoft.comWoloskihttps://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/urn:oasis:names:tc:SAML:2.0:ac:classes:Password'; + + var saml_passport = new SamlPassport({thumbprints: ['3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe0'], + realm: 'spn:408153f4-5960-43dc-9d4f-6b717d772c8d', + checkExpiration: false, checkRecipient: false}); // dont check expiration since we are harcoding the token + var profile = saml_passport.validateSamlAssertion(signedAssertion, function(err, profile) { + if (err) return done(err); + assert.ok(profile); + done(); + }); + + }); + + describe('signed samlp response and assertion from open feide', function () { + var user, r, bod, $; + + // SAMLResponse comes from open.feide https://openidp.feide.no + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback/samlp-signedresponse-signedassertion', + form: { SAMLResponse: 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfZjEzOGQyZTUzMWQ0NjI0ZmNhZmQ4OGJlYWNmN2VjMzkwMzRmMmEzNzRkIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxMy0wNy0wN1QxMTo1NToxOFoiIERlc3RpbmF0aW9uPSJodHRwczovL2xvZ2luLWRldjMuYXV0aDAuY29tOjMwMDAvbG9naW4vY2FsbGJhY2siIEluUmVzcG9uc2VUbz0iX2ZkMDY3N2ExZmRmMTU0Y2JmZGQwIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9vcGVuaWRwLmZlaWRlLm5vPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KICA8ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPgogIDxkczpSZWZlcmVuY2UgVVJJPSIjX2YxMzhkMmU1MzFkNDYyNGZjYWZkODhiZWFjZjdlYzM5MDM0ZjJhMzc0ZCI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+VXJHUURDSGF0eTRjNzZqTW5oWmZZb09qQ1RFPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5uSGZLUDRzbXliTHQxRTdwNVZJMkttUnZtL3RYMEpVRVNGYUN6ejM4M1RDMWpTU2JaODZKSVJYSVdMRXl1WTJCOTJBNHdmdC8zaHhqV2ZBNTNWUFdsYS93UzBEcitRbzUxU2svTzZNek1tbXRXakx2WVZhTDhvQ3lZUFZHSDlyWXZ4cnlnVXFyVkZDZVZhS3U5Y1VwVWpPdXZTYzM1dUovOEJFZUZ1cTdBMm89PC9kczpTaWduYXR1cmVWYWx1ZT4KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ2l6Q0NBZlFDQ1FDWTh0S2FNYzBCTWpBTkJna3Foa2lHOXcwQkFRVUZBRENCaVRFTE1Ba0dBMVVFQmhNQ1RrOHhFakFRQmdOVkJBZ1RDVlJ5YjI1a2FHVnBiVEVRTUE0R0ExVUVDaE1IVlU1SlRrVlVWREVPTUF3R0ExVUVDeE1GUm1WcFpHVXhHVEFYQmdOVkJBTVRFRzl3Wlc1cFpIQXVabVZwWkdVdWJtOHhLVEFuQmdrcWhraUc5dzBCQ1FFV0dtRnVaSEpsWVhNdWMyOXNZbVZ5WjBCMWJtbHVaWFIwTG01dk1CNFhEVEE0TURVd09EQTVNakkwT0ZvWERUTTFNRGt5TXpBNU1qSTBPRm93Z1lreEN6QUpCZ05WQkFZVEFrNVBNUkl3RUFZRFZRUUlFd2xVY205dVpHaGxhVzB4RURBT0JnTlZCQW9UQjFWT1NVNUZWRlF4RGpBTUJnTlZCQXNUQlVabGFXUmxNUmt3RndZRFZRUURFeEJ2Y0dWdWFXUndMbVpsYVdSbExtNXZNU2t3SndZSktvWklodmNOQVFrQkZocGhibVJ5WldGekxuTnZiR0psY21kQWRXNXBibVYwZEM1dWJ6Q0JuekFOQmdrcWhraUc5dzBCQVFFRkFBT0JqUUF3Z1lrQ2dZRUF0OGpMb3FJMVZUbHhBWjJheGlESVRoV2NBT1hkdThLa1ZVV2FOL1Nvb085TzBRUTdLUlVqU0dLTjlKSzY1QUZSRFhRa1dQQXU0SGxuTzRub1lsRlNMbll5RHhJNjZMQ3I3MXg0bGdGSmpxTGVBdkIvR3FCcUZmSVozWUsvTnJoblVxRndadTYzbkxyWmpjVVp4TmFQak9PU1JTRGFYcHYxa2I1azNqT2lTR0VDQXdFQUFUQU5CZ2txaGtpRzl3MEJBUVVGQUFPQmdRQlFZajRjQWFmV2FZZmpCVTJ6aTFFbHdTdElhSjVueXAvcy84QjhTQVBLMlQ3OU1jTXljY1Azd1NXMTNMSGttTTFqd0tlM0FDRlhCdnFHUU4wSWJjSDQ5aHUwRktoWUZNL0dQREpjSUhGQnNpeU1CWENocHllOXZCYVRORUJDdFUzS2pqeUcwaFJUMm1BUTloK2JrUG1PdmxFby9hSDB4UjY4WjlodzRQRjEzdz09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9Il9lYzM1MzRjN2Y2NjYzMjdlNmFmMTU0MzdiZTdiODk5OTU4ZDMwZGY5NzUiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDEzLTA3LTA3VDExOjU1OjE4WiI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vb3BlbmlkcC5mZWlkZS5ubzwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICA8ZHM6UmVmZXJlbmNlIFVSST0iI19lYzM1MzRjN2Y2NjYzMjdlNmFmMTU0MzdiZTdiODk5OTU4ZDMwZGY5NzUiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPlZPWVNVQlZZSUNvTWJwbk5INEVCRHhBUWtKTT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+RW1rR1docVZvZ25vNWhja01UcG9ySHFwT0szVDZpZ2JRVXA2Zmkxc1pvcXFsd3cxSUtmc3REMW1LdzVjM21JcldyNjFnOTh4TFMxLzBnMW5hUWlpT0MzbDl6Y0g3QUFIOVdGWW5JejdGeUE4dmllKzBxTE1Dbno4cVVpZ21HWDNRbEdiQ1QzUHVUNDEzUWlZSm9DT2VXME5zYUpaWUNINUFOWnprSUJsdG9nPTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNpekNDQWZRQ0NRQ1k4dEthTWMwQk1qQU5CZ2txaGtpRzl3MEJBUVVGQURDQmlURUxNQWtHQTFVRUJoTUNUazh4RWpBUUJnTlZCQWdUQ1ZSeWIyNWthR1ZwYlRFUU1BNEdBMVVFQ2hNSFZVNUpUa1ZVVkRFT01Bd0dBMVVFQ3hNRlJtVnBaR1V4R1RBWEJnTlZCQU1URUc5d1pXNXBaSEF1Wm1WcFpHVXVibTh4S1RBbkJna3Foa2lHOXcwQkNRRVdHbUZ1WkhKbFlYTXVjMjlzWW1WeVowQjFibWx1WlhSMExtNXZNQjRYRFRBNE1EVXdPREE1TWpJME9Gb1hEVE0xTURreU16QTVNakkwT0Zvd2dZa3hDekFKQmdOVkJBWVRBazVQTVJJd0VBWURWUVFJRXdsVWNtOXVaR2hsYVcweEVEQU9CZ05WQkFvVEIxVk9TVTVGVkZReERqQU1CZ05WQkFzVEJVWmxhV1JsTVJrd0Z3WURWUVFERXhCdmNHVnVhV1J3TG1abGFXUmxMbTV2TVNrd0p3WUpLb1pJaHZjTkFRa0JGaHBoYm1SeVpXRnpMbk52YkdKbGNtZEFkVzVwYm1WMGRDNXViekNCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBdDhqTG9xSTFWVGx4QVoyYXhpRElUaFdjQU9YZHU4S2tWVVdhTi9Tb29POU8wUVE3S1JValNHS045Sks2NUFGUkRYUWtXUEF1NEhsbk80bm9ZbEZTTG5ZeUR4STY2TENyNzF4NGxnRkpqcUxlQXZCL0dxQnFGZklaM1lLL05yaG5VcUZ3WnU2M25MclpqY1VaeE5hUGpPT1NSU0RhWHB2MWtiNWszak9pU0dFQ0F3RUFBVEFOQmdrcWhraUc5dzBCQVFVRkFBT0JnUUJRWWo0Y0FhZldhWWZqQlUyemkxRWx3U3RJYUo1bnlwL3MvOEI4U0FQSzJUNzlNY015Y2NQM3dTVzEzTEhrbU0xandLZTNBQ0ZYQnZxR1FOMEliY0g0OWh1MEZLaFlGTS9HUERKY0lIRkJzaXlNQlhDaHB5ZTl2QmFUTkVCQ3RVM0tqanlHMGhSVDJtQVE5aCtia1BtT3ZsRW8vYUgweFI2OFo5aHc0UEYxM3c9PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0idXJuOmF1dGgwOmxvZ2luLWRldjMiIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50Ij5fOTVkYThhZjQ4MjY4NmEwY2VjZDY0Y2I3Y2FmOGU4NzFiN2FjMTFkYWUxPC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDEzLTA3LTA3VDEyOjAwOjE4WiIgUmVjaXBpZW50PSJodHRwczovL2xvZ2luLWRldjMuYXV0aDAuY29tOjMwMDAvbG9naW4vY2FsbGJhY2siIEluUmVzcG9uc2VUbz0iX2ZkMDY3N2ExZmRmMTU0Y2JmZGQwIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTMtMDctMDdUMTE6NTQ6NDhaIiBOb3RPbk9yQWZ0ZXI9IjIwMTMtMDctMDdUMTI6MDA6MThaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPnVybjphdXRoMDpsb2dpbi1kZXYzPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMy0wNy0wN1QxMToxMTo0MVoiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTMtMDctMDdUMTk6NTU6MThaIiBTZXNzaW9uSW5kZXg9Il81ZDA2MDZhMGIxZmQ5Nzk4ZDJhMjg3MjE5M2UzOWE5MDdhM2MwYmE0MTUiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1cmkiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPndvbG9za2k8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZ2l2ZW5OYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+TWF0aWFzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9InNuIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+V29sb3NraTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJjbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1cmkiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPk1hdGlhcyBXb2xvc2tpPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dXJpIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5tYXRpYXN3QGdtYWlsLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25QcmluY2lwYWxOYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+d29sb3NraUBybmQuZmVpZGUubm88L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZWR1UGVyc29uVGFyZ2V0ZWRJRCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1cmkiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPjFiMTI0NmQ3MjgxOTdiYjQ3ZDA5MzQyYWE0ZjZjM2Y0N2Y0ZTkyYWU8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idXJuOm9pZDowLjkuMjM0Mi4xOTIwMDMwMC4xMDAuMS4xIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+d29sb3NraTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1cm46b2lkOjIuNS40LjQyIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+TWF0aWFzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVybjpvaWQ6Mi41LjQuNCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1cmkiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPldvbG9za2k8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idXJuOm9pZDoyLjUuNC4zIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+TWF0aWFzIFdvbG9za2k8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idXJuOm9pZDowLjkuMjM0Mi4xOTIwMDMwMC4xMDAuMS4zIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+bWF0aWFzd0BnbWFpbC5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idXJuOm9pZDoxLjMuNi4xLjQuMS41OTIzLjEuMS4xLjYiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dXJpIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj53b2xvc2tpQHJuZC5mZWlkZS5ubzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1cm46b2lkOjEuMy42LjEuNC4xLjU5MjMuMS4xLjEuMTAiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dXJpIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj4xYjEyNDZkNzI4MTk3YmI0N2QwOTM0MmFhNGY2YzNmNDdmNGU5MmFlPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+' } + }, function(err, response, body) { + if(err) return done(err); + if (response.statusCode !== 200) return done(new Error(response.body)); + r = response; + bod = body; + done(); + }); + }); + + it('should validate response and not signature', function(){ + expect(r.statusCode) + .to.equal(200); + }); + + it('should return a valid user', function(){ + var user = JSON.parse(bod); + /* + { + uid: 'woloski', + givenName: 'Matias', + sn: 'Woloski', + cn: 'Matias Woloski', + mail: 'matiasw@gmail.com', + eduPersonPrincipalName: 'woloski@rnd.feide.no', + eduPersonTargetedID: '1b1246d728197bb47d09342aa4f6c3f47f4e92ae', + 'urn:oid:0.9.2342.19200300.100.1.1': 'woloski', + 'urn:oid:2.5.4.42': 'Matias', + 'urn:oid:2.5.4.4': 'Woloski', + 'urn:oid:2.5.4.3': 'Matias Woloski', + 'urn:oid:0.9.2342.19200300.100.1.3': 'matiasw@gmail.com', + 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6': 'woloski@rnd.feide.no', + 'urn:oid:1.3.6.1.4.1.5923.1.1.1.10': '1b1246d728197bb47d09342aa4f6c3f47f4e92ae', + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier': '_95da8af482686a0cecd64cb7caf8e871b7ac11dae1', + issuer: 'https://openidp.feide.no' + } + */ + expect(user['uid']).to.equal('woloski'); + expect(user['givenName']).to.equal('Matias'); + expect(user['sn']).to.equal('Woloski'); + expect(user['cn']).to.equal('Matias Woloski'); + expect(user['uid']).to.equal('woloski'); + expect(user['urn:oid:0.9.2342.19200300.100.1.1']).to.equal('woloski'); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']).to.equal('_95da8af482686a0cecd64cb7caf8e871b7ac11dae1'); + }); + }); + + describe('signed assertion from okta', function () { + var r, bod; + + // SAMLResponse comes from Okta + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback/samlp-okta', + form: { SAMLResponse: 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9hdXRoMDE0NS5hdXRoMC5jb20iIElEPSJpZDgxMzIzMDI4Njg0Njg5ODM4OTkzODY4MzEiIElzc3VlSW5zdGFudD0iMjAxMy0wOC0wM1QyMTo1NDo0My45NDJaIiBWZXJzaW9uPSIyLjAiPjxzYW1sMjpJc3N1ZXIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwOi8vd3d3Lm9rdGEuY29tL2s3eGtocTBqVUhVUFFBWFZNVUFOPC9zYW1sMjpJc3N1ZXI+PHNhbWwycDpTdGF0dXMgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1sMnA6U3RhdHVzPjxzYW1sMjpBc3NlcnRpb24geG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJpZDgxMzIzMDI4Njg1NDEwMTk3NTU0MTQxMjEiIElzc3VlSW5zdGFudD0iMjAxMy0wOC0wM1QyMTo1NDo0My45NDJaIiBWZXJzaW9uPSIyLjAiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI+PHNhbWwyOklzc3VlciBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPmh0dHA6Ly93d3cub2t0YS5jb20vazd4a2hxMGpVSFVQUUFYVk1VQU48L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz48ZHM6UmVmZXJlbmNlIFVSST0iI2lkODEzMjMwMjg2ODU0MTAxOTc1NTQxNDEyMSI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyI+PGVjOkluY2x1c2l2ZU5hbWVzcGFjZXMgeG1sbnM6ZWM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIgUHJlZml4TGlzdD0ieHMiLz48L2RzOlRyYW5zZm9ybT48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPjRHK3V2ZUttdGlCMUVrWTVCQXQrOGxtUXdqST08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+UTgwTjZGVXI1L1lQdEV6UmxSZE1vUHUrYkwwTXNzRHhOVVkreXh5a3pibXhzSTBqb0VvL1NtbVNnWnJEWVFLVGxsWmsvS2Z6Qk1QRlY5eUJINCttRXpDVTVFM3h1Q3M5OWpaemFmY3czSzhtSU1USnkxWUh4amMzNTlkMjdSNXM1MGk5dzVQSHN1c1JvdjBNalFJb0oydzQ4R3k0RW5ZYVZpcUJSM1VWRXFFPTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ25UQ0NBZ2FnQXdJQkFnSUdBVUJHSHhxVU1BMEdDU3FHU0liM0RRRUJCUVVBTUlHUk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFRwpBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFQ2d3RVQydDBZVEVVCk1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFakFRQmdOVkJBTU1DV3RzZFdkc1lXSnpNakVjTUJvR0NTcUdTSWIzRFFFSkFSWU4KYVc1bWIwQnZhM1JoTG1OdmJUQWVGdzB4TXpBNE1ETXlNVE00TXpoYUZ3MDBNekE0TURNeU1UTTVNemhhTUlHUk1Rc3dDUVlEVlFRRwpFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFCkNnd0VUMnQwWVRFVU1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFakFRQmdOVkJBTU1DV3RzZFdkc1lXSnpNakVjTUJvR0NTcUcKU0liM0RRRUpBUllOYVc1bWIwQnZhM1JoTG1OdmJUQ0JuekFOQmdrcWhraUc5dzBCQVFFRkFBT0JqUUF3Z1lrQ2dZRUFzQ0I5bEpUSApxQjd2ZE01amVPSDg0Y1c4dTdJSFl2NC9PQVBZRjBmQlllOXdKeTE5Q2d5TTJPZ2lBU3VBY0l0bkg0V2hCK2lvMlpQd2IvWHdsN1V1CjRYbVVFMGwrbWtDTnVEWXA1ZlhUWnh3djVHNkh2a0F4WFppbzBSazlUMFZFVENyb3hncFM1THhRL28vb3dqUjM5Uzd4elJuajZkZFgKM01xMnlHakt5QmNDQXdFQUFUQU5CZ2txaGtpRzl3MEJBUVVGQUFPQmdRQUIxcUdOcVNOTExXcStSUGNQK3dPYVd0WXBKT0o4L01iWgpFV1dtOS9LS0hLWE02Si96Z1VVSVhaaTNjek1lTytZK1gxNFBSOGxHWG9BSGY1Yi9KYXZHOUZtRnZSbjRmR2E0NVZUVm8yR2ZNTjZLCmFJS0Ywb2JlQ2JZaS9RVWY4QitYaTF0U0lKbTFWQ0tSRTdubmxpUS9UekdhTnVsZ1dleVRiVmtHMC9YOExRPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDI6U3ViamVjdCB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+PHNhbWwyOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+YWRtaW5Aa2x1Z2xhYnMuY29tPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDEzLTA4LTAzVDIxOjU5OjQzLjk0MloiIFJlY2lwaWVudD0iaHR0cHM6Ly9hdXRoMDE0NS5hdXRoMC5jb20iLz48L3NhbWwyOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sMjpTdWJqZWN0PjxzYW1sMjpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMy0wOC0wM1QyMTo0OTo0My45NDNaIiBOb3RPbk9yQWZ0ZXI9IjIwMTMtMDgtMDNUMjE6NTk6NDMuOTQyWiIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sMjpBdWRpZW5jZT5odHRwczovL2F1dGgwMTQ1LmF1dGgwLmNvbTwvc2FtbDI6QXVkaWVuY2U+PC9zYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDI6Q29uZGl0aW9ucz48c2FtbDI6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEzLTA4LTAzVDIxOjU0OjQzLjk0MloiIFNlc3Npb25JbmRleD0iaWQxMzc1NTY2ODgzOTQyLjY4NzYxMDQzNyIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdXRobkNvbnRleHQ+PHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWwyOkF1dGhuQ29udGV4dD48L3NhbWwyOkF1dGhuU3RhdGVtZW50PjxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iUm9sZSIgTmFtZUZvcm1hdD0ibnMiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPkFkbWluPC9zYW1sMjpBdHRyaWJ1dGVWYWx1ZT48L3NhbWwyOkF0dHJpYnV0ZT48L3NhbWwyOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWwyOkFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4=' } + }, function(err, response, body) { + if(err) return done(err); + if (response.statusCode !== 200) return done(new Error(response.body)); + r = response; + bod = body; + done(); + }); + }); + + it('should validate response and not signature', function(){ + expect(r.statusCode) + .to.equal(200); + }); + + it('should return a valid user', function(){ + var user = JSON.parse(bod); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']).to.equal('admin@kluglabs.com'); + expect(user['Role']).to.equal('Admin'); + }); + }); + + describe('signed assertion trying a wrapping attack', function () { + var r; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback/samlp-okta', + form: { SAMLResponse: 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9hdXRoMDE0NS5hdXRoMC5jb20iIElEPSJpZDgxMzIzMDI4Njg0Njg5ODM4OTkzODY4MzEiIElzc3VlSW5zdGFudD0iMjAxMy0wOC0wM1QyMTo1NDo0My45NDJaIiBWZXJzaW9uPSIyLjAiPjxzYW1sMjpJc3N1ZXIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwOi8vd3d3Lm9rdGEuY29tL2s3eGtocTBqVUhVUFFBWFZNVUFOPC9zYW1sMjpJc3N1ZXI+PHNhbWwycDpTdGF0dXMgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1sMnA6U3RhdHVzPjxzYW1sMjpBc3NlcnRpb24geG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJFVklMIiBJc3N1ZUluc3RhbnQ9IjIwMTMtMDgtMDNUMjE6NTQ6NDMuOTQyWiIgVmVyc2lvbj0iMi4wIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiPjxzYW1sMjpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vd3d3Lm9rdGEuY29tL2s3eGtocTBqVUhVUFFBWFZNVUFOPC9zYW1sMjpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+PGRzOlJlZmVyZW5jZSBVUkk9IiNpZDgxMzIzMDI4Njg1NDEwMTk3NTU0MTQxMjEiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiPjxlYzpJbmNsdXNpdmVOYW1lc3BhY2VzIHhtbG5zOmVjPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiIFByZWZpeExpc3Q9InhzIi8+PC9kczpUcmFuc2Zvcm0+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT40Ryt1dmVLbXRpQjFFa1k1QkF0KzhsbVF3akk9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PHNhbWwyOkFzc2VydGlvbiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9ImlkODEzMjMwMjg2ODU0MTAxOTc1NTQxNDEyMSIgSXNzdWVJbnN0YW50PSIyMDEzLTA4LTAzVDIxOjU0OjQzLjk0MloiIFZlcnNpb249IjIuMCIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIj48c2FtbDI6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5IiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cDovL3d3dy5va3RhLmNvbS9rN3hraHEwalVIVVBRQVhWTVVBTjwvc2FtbDI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjaWQ4MTMyMzAyODY4NTQxMDE5NzU1NDE0MTIxIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIj48ZWM6SW5jbHVzaXZlTmFtZXNwYWNlcyB4bWxuczplYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIiBQcmVmaXhMaXN0PSJ4cyIvPjwvZHM6VHJhbnNmb3JtPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+NEcrdXZlS210aUIxRWtZNUJBdCs4bG1Rd2pJPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5RODBONkZVcjUvWVB0RXpSbFJkTW9QdStiTDBNc3NEeE5VWSt5eHlremJteHNJMGpvRW8vU21tU2dackRZUUtUbGxaay9LZnpCTVBGVjl5Qkg0K21FekNVNUUzeHVDczk5alp6YWZjdzNLOG1JTVRKeTFZSHhqYzM1OWQyN1I1czUwaTl3NVBIc3VzUm92ME1qUUlvSjJ3NDhHeTRFbllhVmlxQlIzVVZFcUU9PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDblRDQ0FnYWdBd0lCQWdJR0FVQkdIeHFVTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUdSTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHDQpBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFQ2d3RVQydDBZVEVVDQpNQklHQTFVRUN3d0xVMU5QVUhKdmRtbGtaWEl4RWpBUUJnTlZCQU1NQ1d0c2RXZHNZV0p6TWpFY01Cb0dDU3FHU0liM0RRRUpBUllODQphVzVtYjBCdmEzUmhMbU52YlRBZUZ3MHhNekE0TURNeU1UTTRNemhhRncwME16QTRNRE15TVRNNU16aGFNSUdSTVFzd0NRWURWUVFHDQpFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFDQpDZ3dFVDJ0MFlURVVNQklHQTFVRUN3d0xVMU5QVUhKdmRtbGtaWEl4RWpBUUJnTlZCQU1NQ1d0c2RXZHNZV0p6TWpFY01Cb0dDU3FHDQpTSWIzRFFFSkFSWU5hVzVtYjBCdmEzUmhMbU52YlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXNDQjlsSlRIDQpxQjd2ZE01amVPSDg0Y1c4dTdJSFl2NC9PQVBZRjBmQlllOXdKeTE5Q2d5TTJPZ2lBU3VBY0l0bkg0V2hCK2lvMlpQd2IvWHdsN1V1DQo0WG1VRTBsK21rQ051RFlwNWZYVFp4d3Y1RzZIdmtBeFhaaW8wUms5VDBWRVRDcm94Z3BTNUx4US9vL293alIzOVM3eHpSbmo2ZGRYDQozTXEyeUdqS3lCY0NBd0VBQVRBTkJna3Foa2lHOXcwQkFRVUZBQU9CZ1FBQjFxR05xU05MTFdxK1JQY1Ard09hV3RZcEpPSjgvTWJaDQpFV1dtOS9LS0hLWE02Si96Z1VVSVhaaTNjek1lTytZK1gxNFBSOGxHWG9BSGY1Yi9KYXZHOUZtRnZSbjRmR2E0NVZUVm8yR2ZNTjZLDQphSUtGMG9iZUNiWWkvUVVmOEIrWGkxdFNJSm0xVkNLUkU3bm5saVEvVHpHYU51bGdXZXlUYlZrRzAvWDhMUT09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWwyOlN1YmplY3QgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPmFkbWluQGtsdWdsYWJzLmNvbTwvc2FtbDI6TmFtZUlEPjxzYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxMy0wOC0wM1QyMTo1OTo0My45NDJaIiBSZWNpcGllbnQ9Imh0dHBzOi8vYXV0aDAxNDUuYXV0aDAuY29tIi8+PC9zYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDI6U3ViamVjdD48c2FtbDI6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTMtMDgtMDNUMjE6NDk6NDMuOTQzWiIgTm90T25PckFmdGVyPSIyMDEzLTA4LTAzVDIxOjU5OjQzLjk0MloiIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDI6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDI6QXVkaWVuY2U+aHR0cHM6Ly9hdXRoMDE0NS5hdXRoMC5jb208L3NhbWwyOkF1ZGllbmNlPjwvc2FtbDI6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWwyOkNvbmRpdGlvbnM+PHNhbWwyOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMy0wOC0wM1QyMTo1NDo0My45NDJaIiBTZXNzaW9uSW5kZXg9ImlkMTM3NTU2Njg4Mzk0Mi42ODc2MTA0MzciIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDI6QXV0aG5Db250ZXh0PjxzYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvc2FtbDI6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sMjpBdXRobkNvbnRleHQ+PC9zYW1sMjpBdXRoblN0YXRlbWVudD48c2FtbDI6QXR0cmlidXRlU3RhdGVtZW50IHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDI6QXR0cmlidXRlIE5hbWU9IlJvbGUiIE5hbWVGb3JtYXQ9Im5zIj48c2FtbDI6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5BZG1pbjwvc2FtbDI6QXR0cmlidXRlVmFsdWU+PC9zYW1sMjpBdHRyaWJ1dGU+PC9zYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sMjpBc3NlcnRpb24+PGRzOlNpZ25hdHVyZVZhbHVlPlE4ME42RlVyNS9ZUHRFelJsUmRNb1B1K2JMME1zc0R4TlVZK3l4eWt6Ym14c0kwam9Fby9TbW1TZ1pyRFlRS1RsbFprL0tmekJNUEZWOXlCSDQrbUV6Q1U1RTN4dUNzOTlqWnphZmN3M0s4bUlNVEp5MVlIeGpjMzU5ZDI3UjVzNTBpOXc1UEhzdXNSb3YwTWpRSW9KMnc0OEd5NEVuWWFWaXFCUjNVVkVxRT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNuVENDQWdhZ0F3SUJBZ0lHQVVCR0h4cVVNQTBHQ1NxR1NJYjNEUUVCQlFVQU1JR1JNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUcNCkExVUVDQXdLUTJGc2FXWnZjbTVwWVRFV01CUUdBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVOTUFzR0ExVUVDZ3dFVDJ0MFlURVUNCk1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFakFRQmdOVkJBTU1DV3RzZFdkc1lXSnpNakVjTUJvR0NTcUdTSWIzRFFFSkFSWU4NCmFXNW1iMEJ2YTNSaExtTnZiVEFlRncweE16QTRNRE15TVRNNE16aGFGdzAwTXpBNE1ETXlNVE01TXpoYU1JR1JNUXN3Q1FZRFZRUUcNCkV3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFV01CUUdBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVOTUFzR0ExVUUNCkNnd0VUMnQwWVRFVU1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFakFRQmdOVkJBTU1DV3RzZFdkc1lXSnpNakVjTUJvR0NTcUcNClNJYjNEUUVKQVJZTmFXNW1iMEJ2YTNSaExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBc0NCOWxKVEgNCnFCN3ZkTTVqZU9IODRjVzh1N0lIWXY0L09BUFlGMGZCWWU5d0p5MTlDZ3lNMk9naUFTdUFjSXRuSDRXaEIraW8yWlB3Yi9Yd2w3VXUNCjRYbVVFMGwrbWtDTnVEWXA1ZlhUWnh3djVHNkh2a0F4WFppbzBSazlUMFZFVENyb3hncFM1THhRL28vb3dqUjM5Uzd4elJuajZkZFgNCjNNcTJ5R2pLeUJjQ0F3RUFBVEFOQmdrcWhraUc5dzBCQVFVRkFBT0JnUUFCMXFHTnFTTkxMV3ErUlBjUCt3T2FXdFlwSk9KOC9NYloNCkVXV205L0tLSEtYTTZKL3pnVVVJWFppM2N6TWVPK1krWDE0UFI4bEdYb0FIZjViL0phdkc5Rm1GdlJuNGZHYTQ1VlRWbzJHZk1ONksNCmFJS0Ywb2JlQ2JZaS9RVWY4QitYaTF0U0lKbTFWQ0tSRTdubmxpUS9UekdhTnVsZ1dleVRiVmtHMC9YOExRPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDI6U3ViamVjdCB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+PHNhbWwyOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+YWRtaW5Aa2x1Z2xhYnMuY29tPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDEzLTA4LTAzVDIxOjU5OjQzLjk0MloiIFJlY2lwaWVudD0iaHR0cHM6Ly9hdXRoMDE0NS5hdXRoMC5jb20iLz48L3NhbWwyOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sMjpTdWJqZWN0PjxzYW1sMjpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMy0wOC0wM1QyMTo0OTo0My45NDNaIiBOb3RPbk9yQWZ0ZXI9IjIwMTMtMDgtMDNUMjE6NTk6NDMuOTQyWiIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sMjpBdWRpZW5jZT5odHRwczovL2F1dGgwMTQ1LmF1dGgwLmNvbTwvc2FtbDI6QXVkaWVuY2U+PC9zYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDI6Q29uZGl0aW9ucz48c2FtbDI6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEzLTA4LTAzVDIxOjU0OjQzLjk0MloiIFNlc3Npb25JbmRleD0iaWQxMzc1NTY2ODgzOTQyLjY4NzYxMDQzNyIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdXRobkNvbnRleHQ+PHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWwyOkF1dGhuQ29udGV4dD48L3NhbWwyOkF1dGhuU3RhdGVtZW50PjxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iUm9sZSIgTmFtZUZvcm1hdD0ibnMiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPkVWSUw8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjwvc2FtbDI6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg==' } + }, function(err, response) { + if(err) return done(err); + r = response; + done(); + }); + }); + + it('should return error', function(){ + expect(r.statusCode) + .to.equal(400); + }); + }); + + describe('signed assertion with Signature located in wrong place', function () { + var r; + var SAMLResponse = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfYzc0NjM4NjI3MzFmOGQzYjMzZGIiICBJblJlc3BvbnNlVG89Il8yTjVHR3Aybm1JVENGYmN5R1NLamFRM2FpNkt4OWNBd0RoQkdYMWdBSnl2Q3JsSnZvRVFkakVnVHNmYWpnTTltN2oudy5JOUZ6MWRkVmpaOWxLWkNoY3NwdHA5a3hrQ3VxY3diZU5lLmxKeVZRcEI4aVNhNGF3RllzajlBNXI3UkViNUpwSEg3MkI2ZmVndUhGRlBFOE1hazN1NGhTRUtsOV84bW9pWExkQTU3V1ZoendhOFhZeG40bURzaFNwM1hiMFBFWktPREhNdHhsVlhheWNHWXVNZ0MyMEdwZkNBIiAgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTgtMDMtMDdUMTA6MDk6MzRaIiAgRGVzdGluYXRpb249Imh0dHBzOi8vYXV0aDAtZGV2LWVkLm15LnNhbGVzZm9yY2UuY29tIj48c2FtbDpJc3N1ZXIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+dXJuOmZpeHR1cmUtdGVzdDwvc2FtbDpJc3N1ZXI+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIFZlcnNpb249IjIuMCIgSUQ9Il9GVjZKTGRuS1MyTFVEakNkTkJIRUpYYzBob2lmbEpLdiIgSXNzdWVJbnN0YW50PSIyMDE4LTAzLTA3VDEwOjA5OjM0LjQ1MFoiPjxzYW1sOklzc3Vlcj51cm46Zml4dHVyZS10ZXN0PC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OnVuc3BlY2lmaWVkIj4xMjM0NTY3ODwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIFJlY2lwaWVudD0iaHR0cHM6Ly9hdXRoMC1kZXYtZWQubXkuc2FsZXNmb3JjZS5jb20iIEluUmVzcG9uc2VUbz0iXzJONUdHcDJubUlUQ0ZiY3lHU0tqYVEzYWk2S3g5Y0F3RGhCR1gxZ0FKeXZDcmxKdm9FUWRqRWdUc2ZhamdNOW03ai53Lkk5RnoxZGRWalo5bEtaQ2hjc3B0cDlreGtDdXFjd2JlTmUubEp5VlFwQjhpU2E0YXdGWXNqOUE1cjdSRWI1SnBISDcyQjZmZWd1SEZGUEU4TWFrM3U0aFNFS2w5Xzhtb2lYTGRBNTdXVmh6d2E4WFl4bjRtRHNoU3AzWGIwUEVaS09ESE10eGxWWGF5Y0dZdU1nQzIwR3BmQ0EiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cHM6Ly9hdXRoMC1kZXYtZWQubXkuc2FsZXNmb3JjZS5jb208L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50IHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWVpZGVudGlmaWVyIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+MTIzNDU2Nzg8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZW1haWxhZGRyZXNzIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+amZvb0BnbWFpbC5jb208U2lnbmF0dXJlIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48U2lnbmVkSW5mbz48Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+PFJlZmVyZW5jZSBVUkk9IiNfRlY2SkxkbktTMkxVRGpDZE5CSEVKWGMwaG9pZmxKS3YiPjxUcmFuc2Zvcm1zPjxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L1RyYW5zZm9ybXM+PERpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiIvPjxEaWdlc3RWYWx1ZT5vRjRwY1pielBFcWtOOHhRN1FkY1VlMzJXMkVEdDBqRDJhS2xLOFBKenlZPTwvRGlnZXN0VmFsdWU+PC9SZWZlcmVuY2U+PC9TaWduZWRJbmZvPjxTaWduYXR1cmVWYWx1ZT5nU0s5a245cFhCTFpLZUY1b1gvLyttQUxBKzRNQ1loUUt5SURVZTd4NjdSNlFhMkdPNkNONktCeFJtNEhrUTBmWGoxT0dyVVJPaWdIZTRHVVR6Nkh6d3gvQ0VHYU1UY1FYbldCanlQVVVHb2MrNGtPQm1FazRpSGF6c1hnaTdVM0JEcnVlbFpNY3RXWnp0TWEzWGhhTWZ2c1lCbDFncThHK0VHNFo5TnRpcHNRZUova3IxUFg0K1dGZUVkNExYKzczTlBhblgyQ1cyZnBLZXhtYyt5cHpBN2RyRXJKTTNsbXJhUGxqMFEyUllGSWZWcWc4VVVaQm9VL2VnU1FiS3dra28yYVlnUFJYQkhDRjhveTJQcXhxUWRybkFxUnpqV3FwdVQ5L0ZtZUhlSzg4dHFIVDlMVFl3akZxUTZBZGFHbm1GQU5wZm1uQjRrZ2ZUbzlWa1NsSXc9PTwvU2lnbmF0dXJlVmFsdWU+PEtleUluZm8+PFg1MDlEYXRhPjxYNTA5Q2VydGlmaWNhdGU+TUlJRUR6Q0NBdmVnQXdJQkFnSUpBTHI5SHdnclE3R2VNQTBHQ1NxR1NJYjNEUUVCQlFVQU1HSXhHREFXQmdOVkJBTVREMkYxZEdnd0xtRjFkR2d3TG1OdmJURVNNQkFHQTFVRUNoTUpRWFYwYURBZ1RFeERNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1YyRnphR2x1WjNSdmJqRVFNQTRHQTFVRUJ4TUhVbVZrYlc5dVpEQWVGdzB4TWpFeU1qa3hOVE13TkRkYUZ3MHhNekF4TWpneE5UTXdORGRhTUdJeEdEQVdCZ05WQkFNVEQyRjFkR2d3TG1GMWRHZ3dMbU52YlRFU01CQUdBMVVFQ2hNSlFYVjBhREFnVEV4RE1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQk1LVjJGemFHbHVaM1J2YmpFUU1BNEdBMVVFQnhNSFVtVmtiVzl1WkRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTVppVm1OSGlYTGxkcmdiUzUwT05OT0g3cEoyemc2T2NTTWtZWkdEWkpiT1ovVHF3YXVDNkpPbkk3K3h0a1BKc1FIWlNGSnM0VTBzcmpaS3pEQ21hejJqTEFKRFNoUDJqYVhscmtpMTZuRExQRS8vSUdBZzNCSmd1U21CQ1dwRGJTbTkyVjloU3NFK01oeDZiRGFKaXc4eVErUThpU20wYVRRWnRwNk80SUNNdTAwRVNkaDlOSnFJRUNFTHZQMzFBRFYxWGhqN0lieXlWUERGeE12M29sNUJ5U0U5d3d3T0ZVcS93djdYejlMUmlValV6UE8rTHEzT00zby91Q0RiazdqRDdYckdVdU95ZEFMRDhVTHNYcDRFdURPK25GYmVYQi9pS25kWnludVZLb2tpcnl3bDJuRDJJUDAveW5jZExRWjhCeUl5cVAzRzgyZnEvbDhwN0FzQ0F3RUFBYU9CeHpDQnhEQWRCZ05WSFE0RUZnUVVISTJyVVhlQmpUdjF6QWxsYVBHckhGY0VLMFl3Z1pRR0ExVWRJd1NCakRDQmlZQVVISTJyVVhlQmpUdjF6QWxsYVBHckhGY0VLMGFoWnFSa01HSXhHREFXQmdOVkJBTVREMkYxZEdnd0xtRjFkR2d3TG1OdmJURVNNQkFHQTFVRUNoTUpRWFYwYURBZ1RFeERNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1YyRnphR2x1WjNSdmJqRVFNQTRHQTFVRUJ4TUhVbVZrYlc5dVpJSUpBTHI5SHdnclE3R2VNQXdHQTFVZEV3UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUZCUUFEZ2dFQkFGclhJaEN5NFQ0ZUdyaWtiMFIyd0h2L3VTNTQ4cjNwWnlCVjBDRGJjUndBdGJucEpNdmtHRnFLVnA0cG15b0lEU1ZOSy9qK3NMRXNoQjIwWGZ0ZXpIWnlSSmJDVWJ0S3ZYUTZGc3hvZVpNbE4wSVRZS1Rhb0JaS2hVeHhqOTBvdEFoTkM1OHF3R1VQcXQyTGV3SmhIeUx1Y0trR0oxbVEzYjV4S1o1MzJUb3Vmb3VIOVZMaGlnM0gxS254V28vek1ENktlOGNDazZxTzlodHVoSTA2czNHUUdTMVFXUXRBbW0xN0M2VGZLZ0R3UUZad2hxSFVVWm53S1JIOGdVNk9nWnN2aGdWMUI3SDVtalpjdTU3S01pREJla1U5TUVZMERDVlROM1drbWNUSUk2Njh6THNKcmtOWDZQRWZjazFBTUJiVkU2cEVVS2NXd3EzdWFMdmxBVW89PC9YNTA5Q2VydGlmaWNhdGU+PC9YNTA5RGF0YT48L0tleUluZm8+PC9TaWduYXR1cmU+PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5Kb2huIEZvbzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9naXZlbm5hbWUiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5Kb2huPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3N1cm5hbWUiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5Gb288L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxOC0wMy0wN1QxMDowOTozNC40NTBaIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6dW5zcGVjaWZpZWQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg=='; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback', + form: { SAMLResponse: SAMLResponse } + }, function(err, response) { + if(err) return done(err); + r = response; + done(); + }); + }); + + it('should return error', function(){ + console.log({ body: r.body }); + expect(r.statusCode) + .to.equal(400); + }); + }); + + describe('signed assertion from ping', function () { + var r, bod; + + // SAMLResponse comes from Ping + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback/samlp-ping', + form: { SAMLResponse: 'PHNhbWxwOlJlc3BvbnNlIERlc3RpbmF0aW9uPSJodHRwczovL2xvZ2luLWRldjMuYXV0aDAuY29tOjMwMDAvbG9naW4vY2FsbGJhY2siIEluUmVzcG9uc2VUbz0iXzRhNDMyMzEzNmNhMGFkNDU3OGNiIiBJc3N1ZUluc3RhbnQ9IjIwMTMtMDctMDhUMTk6NDA6MjUuNTIxWiIgSUQ9IklEMzBlNjM2OTcyYzgxYmNkN2E1MzBkMWJkNjc4MmM4YmNiZTU2Y2U4NjEwZDM0ZDJjMDIiIFZlcnNpb249IjIuMCIgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCI+PHNhbWw6SXNzdWVyIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPlBpbmdDb25uZWN0PC9zYW1sOklzc3Vlcj48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpBc3NlcnRpb24gVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTMtMDctMDhUMTk6NDA6MjUuNTIxWiIgSUQ9IklENzc1MzVlNjc2YjM0ZDQyNzAzMWM3NTM5ODk2Nzg5YzE5NTM4ZTdlNmRmNTY5YWQ4MDIiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sOklzc3Vlcj5QaW5nQ29ubmVjdDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CjxkczpTaWduZWRJbmZvPgo8ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgo8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+CjxkczpSZWZlcmVuY2UgVVJJPSIjSUQ3NzUzNWU2NzZiMzRkNDI3MDMxYzc1Mzk4OTY3ODljMTk1MzhlN2U2ZGY1NjlhZDgwMiI+CjxkczpUcmFuc2Zvcm1zPgo8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgo8L2RzOlRyYW5zZm9ybXM+CjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPgo8ZHM6RGlnZXN0VmFsdWU+elRZdENWTVpBUDJYZGFJRDM3VlMyZ1MrTEwwPTwvZHM6RGlnZXN0VmFsdWU+CjwvZHM6UmVmZXJlbmNlPgo8L2RzOlNpZ25lZEluZm8+CjxkczpTaWduYXR1cmVWYWx1ZT4KUUNaRGtMMXRzZzFISDNtQVd3UDJzYWlRVFNldTBsTTdmdXNCQmlsL3RDdGpkU2xlZXgra1JETFpvY2p1SytVcDEvN0poS0NOVVE2SApOcVd0cWZHVVJpcUpaKzI5UVoxdzZibU93YXVzZzFiUkJGb0VtUkd0cER4dTBwNHNXemlOdzJ0eU81b1ZWSDYwaEFmZllHTjcweUg0CkUxd2t1emVhRVhOWFhGaEFKeHNOVGo1SkdEZmNld1U5UjQvVW1yTlVYWTdrdkJRV3BRcmI1VE5TNjNrL0hhaEdsN3pPNzJla1FVK04KU0FUU0Eyb2hJU09obVBTbUVyQVVaQkI2Z0FSQ2srT2xWRld0VS9NUnFFMlo5UWFXNndkYlBnVkg0d2JYbnA4YkcxdTQ0VHk5bUFDWApiKzhXT0F3NHBYTkxXYzVZK2dKaVFyaUdyb3p6b0pQaDVGL3FDZz09CjwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPgo8ZHM6WDUwOURhdGE+CjxkczpYNTA5Q2VydGlmaWNhdGU+Ck1JSUZ4akNDQks2Z0F3SUJBZ0lRYXFiblNYTklDQWRmVFo4Sm9pcU9VakFOQmdrcWhraUc5dzBCQVFVRkFEQmlNUXN3Q1FZRFZRUUcKRXdKVlV6RWhNQjhHQTFVRUNoTVlUbVYwZDI5eWF5QlRiMngxZEdsdmJuTWdUQzVNTGtNdU1UQXdMZ1lEVlFRREV5ZE9aWFIzYjNKcgpJRk52YkhWMGFXOXVjeUJEWlhKMGFXWnBZMkYwWlNCQmRYUm9iM0pwZEhrd0hoY05NVEl3TXpBeU1EQXdNREF3V2hjTk1UUXdPREk0Ck1qTTFPVFU1V2pDQnp6RUxNQWtHQTFVRUJoTUNWVk14RGpBTUJnTlZCQkVUQlRnd01qQXlNUXN3Q1FZRFZRUUlFd0pEVHpFUE1BMEcKQTFVRUJ4TUdSR1Z1ZG1WeU1SSXdFQVlEVlFRSkV3bFRkV2wwWlNBeE1EQXhHVEFYQmdOVkJBa1RFREV3TURFZ01UZDBhQ0JUZEhKbApaWFF4SWpBZ0JnTlZCQW9UR1ZCcGJtY2dTV1JsYm5ScGRIa2dRMjl5Y0c5eVlYUnBiMjR4R0RBV0JnTlZCQXNURDFObFkzVnlaU0JNCmFXNXJJRk5UVERFbE1DTUdBMVVFQXhNY2MzTnZMbU52Ym01bFkzUXVjR2x1WjJsa1pXNTBhWFI1TG1OdmJUQ0NBU0l3RFFZSktvWkkKaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKZjF1UnZkSTJIdHJtNFNLUTZJd0xtSk9MeXJZdDNvdlJHaUJjejVoLzdmM3BqQwp4UjNYaml1dlVzZjR3T0pVWDAwRzNhcXpvK1o3VGVIRkJPOXA1UnBPWlFwK01tRDFGWko4ZlNUQVZjTDhUZHZKRjFKWUNaZEt3bCtMCk1MS0ZtWnZRMFljR0NOcEVmL1NRRlI1VlJUZzNmekV3eWdMMklsRFpYbXJ6RklBTk04R0JGUFNrZ3JoZitad3lsNnY1TVo2VEhQemUKSlpzMkZnek05MjVkZUNhZDUwMWZJOWtsdnRyRVN6NCtrZUxITEN3TFU2dCtKYW5vMzJneHZLYzNLZ2VyWlJkZ2pEcGI1MVpjRE10WQo4dTZBMXVVUGpnSmdPTHkvVFd5NUpXaE4vQmlwQkNGWEFXRTZCRDdKb3lmUEJTTjlRaE51cWR2WHM3NVhyRmxmNUk4Q0F3RUFBYU9DCkFnZ3dnZ0lFTUI4R0ExVWRJd1FZTUJhQUZEeEI0bzhJQ0tsTUpZbU5iY1U0MFB5RmpHSVhNQjBHQTFVZERnUVdCQlRRdHNIWU0wU0UKK0JwUGdUcUFudDhtb05CeHp6QU9CZ05WSFE4QkFmOEVCQU1DQmFBd0RBWURWUjBUQVFIL0JBSXdBREFkQmdOVkhTVUVGakFVQmdncgpCZ0VGQlFjREFRWUlLd1lCQlFVSEF3SXdhd1lEVlIwZ0JHUXdZakJnQmd3ckJnRUVBWVlPQVFJQkF3RXdVREJPQmdnckJnRUZCUWNDCkFSWkNhSFIwY0RvdkwzZDNkeTV1WlhSM2IzSnJjMjlzZFhScGIyNXpMbU52YlM5c1pXZGhiQzlUVTB3dGJHVm5ZV3d0Y21Wd2IzTnAKZEc5eWVTMWpjSE11YW5Od01Ib0dBMVVkSHdSek1IRXdOcUEwb0RLR01HaDBkSEE2THk5amNtd3VibVYwYzI5c2MzTnNMbU52YlM5TwpaWFIzYjNKclUyOXNkWFJwYjI1elgwTkJMbU55YkRBM29EV2dNNFl4YUhSMGNEb3ZMMk55YkRJdWJtVjBjMjlzYzNOc0xtTnZiUzlPClpYUjNiM0pyVTI5c2RYUnBiMjV6WDBOQkxtTnliREJ6QmdnckJnRUZCUWNCQVFSbk1HVXdQQVlJS3dZQkJRVUhNQUtHTUdoMGRIQTYKTHk5M2QzY3VibVYwYzI5c2MzTnNMbU52YlM5T1pYUjNiM0pyVTI5c2RYUnBiMjV6WDBOQkxtTnlkREFsQmdnckJnRUZCUWN3QVlZWgphSFIwY0RvdkwyOWpjM0F1Ym1WMGMyOXNjM05zTG1OdmJUQW5CZ05WSFJFRUlEQWVnaHh6YzI4dVkyOXVibVZqZEM1d2FXNW5hV1JsCmJuUnBkSGt1WTI5dE1BMEdDU3FHU0liM0RRRUJCUVVBQTRJQkFRQ0h1elNEaHg0Ukd5N1JiQ0FJMG1maWR2bXR6ZmFMVm9LaUIzVFoKSlVmNmpreXF5NmN3bjA5bjQxdW1kdUpZRHU4K2p2ODFKZUxXdGpabWxYZmhzb3ZhQlRJRVd4aUkxTlFLNlY5Rjk3ZDM3bHF5c3ZJbQprSUliaFVKaEJvSW5ZUldLM1dxYXd6V295Vjh2eFAxaDdNam81c1BOZ3FmTnNVVm5iOVBJODA0bFg3UEZZSHJxS0tudXJxb09kTHJvCnpGU2pBeUU0NElFNWdHWWVqN05SbE5EY1VRcEZ3Yjd6MkJqTVJtQ3RReTA2VlRjSFVlZDdNcWc5WHNNVHA4WXlDbW9uWkFlTGpXYTkKZnFDcVFJZzZvT3BTNzhKeXN4bGVFM3YvUWtYd0tmdGwzZGFiT2dwL1hDMzF6VW1MRDhqTzZLYmRpTzhrQ1B1TFFIUEl2c0ZvYXRDSQo8L2RzOlg1MDlDZXJ0aWZpY2F0ZT4KPC9kczpYNTA5RGF0YT4KPGRzOktleVZhbHVlPgo8ZHM6UlNBS2V5VmFsdWU+CjxkczpNb2R1bHVzPgpsL1c1RzkwalllMnViaElwRG9qQXVZazR2S3RpM2VpOUVhSUZ6UG1IL3QvZW1NTEZIZGVPSzY5U3gvakE0bFJmVFFiZHFyT2o1bnROCjRjVUU3Mm5sR2s1bENuNHlZUFVWa254OUpNQlZ3dnhOMjhrWFVsZ0psMHJDWDRzd3NvV1ptOURSaHdZSTJrUi85SkFWSGxWRk9EZC8KTVRES0F2WWlVTmxlYXZNVWdBMHp3WUVVOUtTQ3VGLzVuREtYcS9reG5wTWMvTjRsbXpZV0RNejNibDE0SnAzblRWOGoyU1crMnNSTApQajZSNHNjc0xBdFRxMzRscWVqZmFERzhwemNxQjZ0bEYyQ01PbHZuVmx3TXkxank3b0RXNVErT0FtQTR2TDlOYkxrbGFFMzhHS2tFCklWY0JZVG9FUHNtako4OEZJMzFDRTI2cDI5ZXp2bGVzV1Yva2p3PT0KPC9kczpNb2R1bHVzPgo8ZHM6RXhwb25lbnQ+QVFBQjwvZHM6RXhwb25lbnQ+CjwvZHM6UlNBS2V5VmFsdWU+CjwvZHM6S2V5VmFsdWU+CjwvZHM6S2V5SW5mbz4KPC9kczpTaWduYXR1cmU+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnRlc3R1c2VyMUB0ZXN0aWRwLmNvbm5lY3QucGluZ2lkZW50aXR5LmNvbTwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iXzRhNDMyMzEzNmNhMGFkNDU3OGNiIiBOb3RPbk9yQWZ0ZXI9IjIwMTMtMDctMDhUMjA6MTA6MjUuNTIxWiIgUmVjaXBpZW50PSJodHRwczovL2xvZ2luLWRldjMuYXV0aDAuY29tOjMwMDAvbG9naW4vY2FsbGJhY2siLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdE9uT3JBZnRlcj0iMjAxMy0wNy0wOFQyMDoxMDoyNS41MjFaIiBOb3RCZWZvcmU9IjIwMTMtMDctMDhUMTk6MzA6MjUuNTIxWiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT51cm46YXV0aDA6bG9naW4tZGV2Mzwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTMtMDctMDhUMTk6NDA6MjUuNTIyWiIgU2Vzc2lvbkluZGV4PSJsTGJTYlhpZmFPREx3T2hCZ1AyZk5PdVc5RmoiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48c2FtbDpBdXRoZW50aWNhdGluZ0F1dGhvcml0eT50ZXN0aWRwLmNvbm5lY3QucGluZ2lkZW50aXR5LmNvbTwvc2FtbDpBdXRoZW50aWNhdGluZ0F1dGhvcml0eT48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIj48c2FtbDpBdHRyaWJ1dGUgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyIgTmFtZT0iUGluZ09uZS5pZHBpZCI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+YjE0ZWIwYjYtYTMzYS00MTRkLTljNzctMDEzMWUzMjRhNWI3PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiIE5hbWU9Im5hbWVpZCI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+dGVzdF9uYW1laWQ8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyIgTmFtZT0iUGluZ09uZS5BdXRoZW50aWNhdGluZ0F1dGhvcml0eSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+dGVzdGlkcC5jb25uZWN0LnBpbmdpZGVudGl0eS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4=' } + }, function(err, response, body) { + if(err) return done(err); + if (response.statusCode !== 200) return done(new Error(response.body)); + r = response; + bod = body; + done(); + }); + }); + + it('should validate response and not signature', function(){ + expect(r.statusCode) + .to.equal(200); + }); + + it('should return a valid user', function(){ + var user = JSON.parse(bod); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']).to.equal('testuser1@testidp.connect.pingidentity.com'); + }); + }); + + it('should validate an assertion from adfs', function (done) { + var signedAssertion = 'urn:auth0:auth0john@fabrikam.comurn:oasis:names:tc:SAML:1.0:cm:bearerjohn@fabrikam.comJohn FabrikamJohnFabrikamjohn@fabrikam.comurn:oasis:names:tc:SAML:1.0:cm:bearer8Yi8+vbZagbCsopdmXFjeFvexHlkHYAViSf9w3yFbWo=SzQaNU4uo4IPJTsK3DZkYNx1FzpAoIxXyWiOMJyuUScYbvUHMjxoPsh6KJrkLwUIGnv07TpTFKrhjA/tGzLCwPMOWlpGp66N/U9wWe3vshHyW34/oAq14EwptjmXEPpHjR2P+giJAUJ0VTXIvbTsGNLuDVV/px43CoIX2D6jc8yt8VffAMX4R+WzI2a6QRMqglTbxzFEfcJC1yK9jT/UzjRIKe9bMS2T8kupDaGOWYj4XMh9BkIVXV40jJakss+cF4wjO/LWfpbwZwMzfOXBQV+W6O5X/HfTod/4zzmdFKjArx6vXQl7vCRr9AXUGgbPWdtSVK7HSMCyjZiAOGcr0Q==MIIC5DCCAcygAwIBAgIQGmfE0ae34q1IXJvpfhwYVDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNBREZTIFNpZ25pbmcgLSB0ZXN0LWFkZnMuYXV0aDEwLmNvbTAeFw0xMzAyMTIwMzU3MzlaFw0xNDAyMTIwMzU3MzlaMC4xLDAqBgNVBAMTI0FERlMgU2lnbmluZyAtIHRlc3QtYWRmcy5hdXRoMTAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuNaTwlMjKeXVabhVQJjmjousrLuYi5hxELIQX80ZT3/kTsRUQaP6AoCMEYjofcV6QnxQeHxHJgzcy5t4bmHozIsLYlroH7PaDNqXLnX+6ivAX1nFa9IOeaw3X206oGmCSo6pJxbW8LwXjYC7BYrINitm/mgcOrypbasADvO1j+fyjkBTFPuF2+La4ehwGCKdrvZZF0lDKiHPbCI8xqH7HOgX+QLtnhK2WclDIpcPd2eeaVq1fB8XBwTXNOv2ZdaEi2i3mDXQoa8wZVozqs5h6OKVXl7hNQH2/qUDAZc15kZcR9Zcxyo5v0GtqYS3J70Q+8HkuQfYTBGR+Cum9S+bfQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAVf930SJfJquM/4A3vibsmjRuCytHeEXLrEyt0j/NBDnyLqFMoL7qILfqtK3oSmQNb6PRQiLLFJlIz7fnkj6k3hm4qpJJvjyqcxYDeW6SK8yP7zKNGMeyIMs/humVgXWALkTF6PoFIfFo2lfnHAzHqPE06WN+hcYHRBjuR5/T+58l2LH9vJjPHuceGkWXyQCB/Hd2riNElXODIBEKzzKL923rm3oPaH/Un8TYnpyRRVKLd40DiptbY/E7YpzWrOsUppjsm0pUhdvBYjihLc6PhALzoUC3GwMjUK0T70gqRoGzsiqRIjIEC35QBGKavzhya2DLTZK3Pf70eJpkp1bGL'; + + var saml_passport = new SamlPassport({thumbprints: ['C9018666E764613366C20BC011D947B39BED236B'], + realm: 'urn:auth0:auth0', + checkExpiration: false, + checkRecipient: false}); // dont check expiration since we are harcoding the token + var profile = saml_passport.validateSamlAssertion(signedAssertion, function(err, profile) { + if (err) return done(err); + assert.ok(profile); + done(); + }); + + }); + + it('should validate an assertion with an umlaut character entity in the xml', function (done) { + var signedAssertion = 'https://aai-logon.ethz.ch/idp/shibbolethjVMwKZ5O3hXfOf6tkVan2hnPW2w=nq5nJangoli5J6uBF/sEeYyKL7+xepbsDmjT6mpggLmba6yR+lQaZmAGnti8nhZUPyXwZfZS3d9oH4upbRg56jdVVcPaZUhYOPW2T2etm7lxxaDlHDJo/E40KnBtGMn6Oxz23hXUrc6p6K4FFLCQwmsE3ZZlP/u8DcqKNl5X/D5udcCV75mjxnVKWuXu34Xw4uQEQBb+6UfGjDN1/91M6U3ZZ0iOSRsBC7+SYLVMbDZqGveioKjZMPBuHmoBwQxsCixu1var3LNyCFVRo0LV9qA5DhA5lyH209+kFsN9vqzHKkiOF+Wua+Ngh2oR/48CWfTOjDuvRpje1bICIwwCQg==MIIFjzCCBHegAwIBAgIUZ+QtvaEucMtOcruHlzQrEDH92FMwDQYJKoZIhvcNAQEFBQAwazELMAkG\nA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHzAdBgNVBAsTFnd3dy5xdW92YWRp\nc2dsb2JhbC5jb20xIDAeBgNVBAMTF1F1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBMB4XDTEzMDQxNzA4\nMDYwNFoXDTE1MDQxNzA4MDYwNFowYzELMAkGA1UEBhMCQ0gxEDAOBgNVBAgTB1p1ZXJpY2gxEDAO\nBgNVBAcTB1p1ZXJpY2gxFDASBgNVBAoTC0VUSCBadWVyaWNoMRowGAYDVQQDExFhYWktbG9nb24u\nZXRoei5jaDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOJWLI4vWx5HnqUvkBDm5Egp\nUg8yOlL3HbS0Y62/k77R2W9wxNczcR79wUBl2cNDCF/LxzdY1ml2u2skbZy4tqtmcvHVrwM5RVDb\n3jpjUhzBlD5rkpxgut2zFmNsahXzceD9dzsTvq7MUq6YgW6iRY3wNbes7ZgRtdkCz+vbiB52iTES\nZ2lo6fBn69eiqywUhQ5t/K4jGqpSUf1DITz//lMWRveagVyUq342JONxo93nt6x6ewGg+Qo8yCuC\nj4VehpncHYV0oNI2sSncKPm23Z4TNxPDalSaq8R5nKhueG+FHX7Ks8hWYSf42m2rrZLTumv2Ry8H\nFrPFkI7kuSFwVRECAwEAAaOCAjEwggItMHQGCCsGAQUFBwEBBGgwZjAqBggrBgEFBQcwAYYeaHR0\ncDovL29jc3AucXVvdmFkaXNnbG9iYWwuY29tMDgGCCsGAQUFBzAChixodHRwOi8vdHJ1c3QucXVv\ndmFkaXNnbG9iYWwuY29tL3F2c3NsaWNhLmNydDCBtQYDVR0RBIGtMIGqghFhYWktbG9nb24uZXRo\nei5jaIIPdmNpcGhlci5ldGh6LmNogg92Y2Flc2FyLmV0aHouY2iCD3ZjdXJ0ZXIuZXRoei5jaIIP\ndmNvcHBlci5ldGh6LmNogg92Y2Vuc29yLmV0aHouY2iCEmxkYXBzLWluZm8uZXRoei5jaIIPbGlu\ndGVzdC5ldGh6LmNogRt2bGFkaXNsYXYubmVzcG9yQGlkLmV0aHouY2gwUQYDVR0gBEowSDBGBgwr\nBgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2dsb2JhbC5jb20v\ncmVwb3NpdG9yeTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC\nMB8GA1UdIwQYMBaAFDJNoU/q8K6Ztu6bByyECBFQi+J+MDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6\nLy9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2c3NsaWNhLmNybDAdBgNVHQ4EFgQUUrfY5AJdnN5W\n9TTyrVObbQEoH/cwDQYJKoZIhvcNAQEFBQADggEBAJHQIjLbalw9LF9wIjhhOsEsaf/Bd8dSKcb2\nICLC16TyetuTTJfqHqHr3QiAcrSNKOxqoFBX51t7oNyd3n1BGxJeYmpoyKHKmViUF9mJWBKxSvfW\njmYA7M/LptNX+aUz0fPntCokjH5pPAk3n5YYf2gTFOmRbZDdvNxQ0+o5EkRKkxLDAYM7HlJshWfK\nyY8ZKiPSx28ebXORGzW/VC5VunURFPmhvy5hUFo2qFhGhkQZD1Tg5uN+vd7KywgXLiQKWFDweOxY\nkFuTatM9peWNaapAuaYL8D6q/pn6q76cDKiMjTLp1siQsVVzFAZNjywOve5tdqB/Qo7zwX7TggF1\nmrQ=_e132eb870c4a912c56e1bafeb5257b35urn:auth0:fmi-testurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportmemberstaffstudentGnüggeRobertethz.ch187624@ethz.chuniversity37J7PjSu8hkThPDMZOfZLtca0Ag=robert.gnuegge@bsse.ethz.ch'; + + var saml_passport = new SamlPassport({thumbprints: ['42FA24A83E107F6842E05D2A2CA0A0A0CA8A2031'], + realm: 'urn:auth0:fmi-test', + recipientUrl: 'https://fmi-test.auth0.com/login/callback', + checkExpiration: false}); // dont check expiration since we are harcoding the token + var profile = saml_passport.validateSamlAssertion(signedAssertion, function(err, profile) { + if (err) return done(err); + assert.ok(profile); + done(); + }); + + }); + + it('should validate a response whose assertion contains unicode character entities in the xml', function (done) { + var signedResponse = 'https://app.onelogin.com/saml/metadata/751422FJbpoVhnf25bH9AM060fFF5xVV8=M1D/IRkUxGyVGqmsYqhtQacQUy5NdjQVqzWcAQOQvrCEH+enonwSPpModMoQrp6vVCdfUOl7Zm/4piOh+LJ6gy26qZsZuUABLhFXkXjSB43es+RnjJjyU2eIJbsU4vtO0UHDKA9qUZkP4DWXIEa050ejDVgBVFlcjT0b/WNpQYYYD0wszC7PlqZmPqMBrjZk7KLM2BNDq/3j+DK7IXhEy0B9OkOkHf6nbFTMU2lXXGzwFqvg9ofoygTyJ1UgZIAeFi3DAkeJTQJkyetS7C3nwi7bxnkxGakYThngF7urkGSJiJPVW3ZlUAKvwEpYdQ0fkj/lpfbnwlmZJirkXvYPBw==MIIEGjCCAwKgAwIBAgIULmFf91+0qo061PisJLfrqhebGTowDQYJKoZIhvcNAQEFBQAwWTELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCU15Q29tcGFueTEVMBMGA1UECwwMT25lTG9naW4gSWRQMR8wHQYDVQQDDBZPbmVMb2dpbiBBY2NvdW50IDg1NDA2MB4XDTE2MDUzMDIyMTkzN1oXDTIxMDUzMTIyMTkzN1owWTELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCU15Q29tcGFueTEVMBMGA1UECwwMT25lTG9naW4gSWRQMR8wHQYDVQQDDBZPbmVMb2dpbiBBY2NvdW50IDg1NDA2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyZyU1dVPmckuDUWLkIGt8HDH8gu6LjKhoOPIwm7qiZVxtm1QhGkOwgUY71lMJLOhWXsjZKqxstdK0Ni7VrgnU5fL7AfGsd5Jf2RIaiUgiRwIN2MFkF3+Gw1mXiPA30Xs2LqBT5PAa+wHooQR0uWDHRlh2pj/qfM6TrsaXDk73KyQAQXYoY29RtB+a0mwwdGKQF6R5MyrMcsssMdkwlyKptHPzEo6JYUObDxBEbu2jEtyhj8FVz9leqB28MYO2C0TDWEDDzcRG+2LPp4JZMEF3d00trQy8F02VnU9rVCs4/VBswBo5+ayUHPOU6u2uPB26p0PubU0Kz+Tfi2AmpAmbQIDAQABo4HZMIHWMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFL7WwnT9l950ZweaTpUjS3Tcp2eCMIGWBgNVHSMEgY4wgYuAFL7WwnT9l950ZweaTpUjS3Tcp2eCoV2kWzBZMQswCQYDVQQGEwJVUzESMBAGA1UECgwJTXlDb21wYW55MRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODU0MDaCFC5hX/dftKqNOtT4rCS366oXmxk6MA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAoPTsWDFG6U+QgzBEBllQnkHYNTIG98938stmu9StEd7A75OcsfygXP1cXqS+0zbtVooYSd+wBUgGvPOw+YUJLFn/MOVoqpACJo1Q8BRN93nNR/YG9nysNr2PYKlO3MPVh6eEkc5g5gXUdZ764tbBG22VvUiZyeS1HbNQGl5OD6ULHw6k5cq/8IO3K65/QtAubZ4I1rlYjfRwM46NlSoiU+9XPukMeMyb3Ddxfud6rttIBTSNs+HbqbMm9Y7ZBU1FQhPEhhctgrRIVoyP9StVUR8IGXqEBIKTOyjAG52HnoTyQmL/6dkoQk1GMP8yG7Pmwf5Dej9lgP6ffpCx1pIV8g==https://app.onelogin.com/saml/metadata/751422kchen七味@shichimitogarashi.orgurn:auth0:dse-investigations:OneLoginurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportNon-ASCIIkchen七味@shichimitogarashi.orgCharacters'; + + var samlOptions = { + thumbprints: ['B7FB3723CF22C0315644AB242EBABFFD2A95CBD4'], + realm: 'urn:auth0:dse-investigations:OneLogin', + recipientUrl: 'https://dse-investigations.auth0.com/login/callback?connection=OneLogin', + checkExpiration: false // dont check expiration since we are harcoding the token + }; + + var samlpOptions = { + thumbprints: samlOptions.thumbprints, + protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + destinationUrl: samlOptions.recipientUrl + }; + + var saml_passport = new SamlPassport(samlOptions); + var sp = new samlp(samlpOptions, saml_passport); + + sp.validateSamlResponse(signedResponse, function(err, profile) { + if (err) return done(err); + assert.ok(profile); + expect(profile).to.have.property('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier', 'kchen七味@shichimitogarashi.org'); + done(); + }); + }); + + it('should validate an assertion with \\r\\n', function (done) { + var response = 'PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERl\r\nc3RpbmF0aW9uPSJodHRwczovL3B3Y3Rlc3QuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5l\r\nY3Rpb249U2l0ZW1pbmRlckRldiIgSUQ9Il9iOTRmZjU0ZmM5OWQzNDMwMDQyMWZiZGMzMjFjNjMz\r\nNmVjNWQiIEluUmVzcG9uc2VUbz0iXzRhZjhhZGQwZTc2YTIwNzI2Njg0IiBJc3N1ZUluc3RhbnQ9\r\nIjIwMTQtMDgtMTJUMDM6MTg6NTFaIiBWZXJzaW9uPSIyLjAiPg0KICAgIDxuczE6SXNzdWVyIHht\r\nbG5zOm5zMT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgRm9ybWF0PSJ1\r\ncm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHBzOi8v\r\ncGFydG5lcnNoaXAtZGV2LnB3Y2ludGVybmFsLmNvbTwvbnMxOklzc3Vlcj4NCiAgICA8U3RhdHVz\r\nPg0KICAgICAgICA8U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4w\r\nOnN0YXR1czpTdWNjZXNzIi8+DQogICAgPC9TdGF0dXM+DQogICAgPG5zMjpBc3NlcnRpb24geG1s\r\nbnM6bnMyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzEyODkx\r\nNGVhM2FiYmJlM2IxNzgzYzY4OWNiZWI3NzViNTQ4MiIgSXNzdWVJbnN0YW50PSIyMDE0LTA4LTEy\r\nVDAzOjE4OjUxWiIgVmVyc2lvbj0iMi4wIj4NCiAgICAgICAgPG5zMjpJc3N1ZXIgRm9ybWF0PSJ1\r\ncm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHBzOi8v\r\ncGFydG5lcnNoaXAtZGV2LnB3Y2ludGVybmFsLmNvbTwvbnMyOklzc3Vlcj48ZHM6U2lnbmF0dXJl\r\nIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4NCjxkczpTaWdu\r\nZWRJbmZvPg0KPGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3\r\nLnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4NCjxkczpTaWduYXR1cmVNZXRob2QgQWxn\r\nb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4NCjxk\r\nczpSZWZlcmVuY2UgVVJJPSIjXzEyODkxNGVhM2FiYmJlM2IxNzgzYzY4OWNiZWI3NzViNTQ4MiI+\r\nDQo8ZHM6VHJhbnNmb3Jtcz4NCjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3Lncz\r\nLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPg0KPGRzOlRyYW5zZm9y\r\nbSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPg0K\r\nPC9kczpUcmFuc2Zvcm1zPg0KPGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cu\r\ndzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+DQo8ZHM6RGlnZXN0VmFsdWU+cFV1aHZjMk1D\r\nVXBjQUZMbVM1a1FWR0tCbzhFPTwvZHM6RGlnZXN0VmFsdWU+DQo8L2RzOlJlZmVyZW5jZT4NCjwv\r\nZHM6U2lnbmVkSW5mbz4NCjxkczpTaWduYXR1cmVWYWx1ZT4NCml0UHp5em4xd0IrM3IwQzRieXhV\r\nQzRBL082ZVpDOW1tUnM4c0UwTU5SbVlJVEw1NC9DTEhKMlRlbVhFb2JDMjFvVXZRRGxiSTVJRXYN\r\nCkZvSmFGVXVBVW5rcVFPalVQT1MzbERNM0dITkFkc2ZTWElmMGpGQ1B6Qno3UDlYRzBCZGU0QW9W\r\ndUgvU1J1V29qNksrV1AxSHdYOEgNClpDZVFMc1FBZGNpWTV6SlFnUEJFK24vdzRzem1veDY2Vyt6\r\nNjlRMUVrczdoNkYwQ3RBemlKOW9uR2VTUU5UKzJWelJYUEhDMndDSnUNCklQcDQ1MDJnTFlnNExk\r\ncEN0eHZNd3BiR2RLbjdZZDY1SHFrUjhZKzFMT01ibVlPYk04bWtYN2s5Uy9Cb1NBSDlybjMxUU5v\r\nSVcwbGcNCjZiQzhiRzBXcVlIblFFazN4QnY3SUt2SUFVWFNFL3VUR285dnZBPT0NCjwvZHM6U2ln\r\nbmF0dXJlVmFsdWU+DQo8ZHM6S2V5SW5mbz4NCjxkczpYNTA5RGF0YT4NCjxkczpYNTA5Q2VydGlm\r\naWNhdGU+DQpNSUlHQURDQ0JPaWdBd0lCQWdJS0hvVmR2Z0FEQUJLL2xEQU5CZ2txaGtpRzl3MEJB\r\nUVVGQURCVU1STXdFUVlLQ1pJbWlaUHlMR1FCDQpHUllEWTI5dE1STXdFUVlLQ1pJbWlaUHlMR1FC\r\nR1JZRGNIZGpNU2d3SmdZRFZRUURFeDlRY21salpYZGhkR1Z5YUc5MWMyVkRiMjl3DQpaWEp6SUVs\r\nemMzVnBibWN4TUI0WERURXpNRFV3TXpFNU16UXdORm9YRFRFMU1URXdNekU1TkRRd05Gb3dnYlF4\r\nQ3pBSkJnTlZCQVlUDQpBbFZUTVJBd0RnWURWUVFJRXdkR2JHOXlhV1JoTVE0d0RBWURWUVFIRXdW\r\nVVlXMXdZVEVqTUNFR0ExVUVDaE1hVUhKcFkyVjNZWFJsDQpjbWh2ZFhObFEyOXZjR1Z5Y3lCTVRG\r\nQXhEREFLQmdOVkJBc1RBMGRVVXpFb01DWUdBMVVFQXhNZmNHRnlkRzVsY25Ob2FYQXRaR1YyDQpM\r\nbkIzWTJsdWRHVnlibUZzTG1OdmJURW1NQ1FHQ1NxR1NJYjNEUUVKQVJZWGJtRjJhVzR1WW1oaGRH\r\nbGhRSFZ6TG5CM1l5NWpiMjB3DQpnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtB\r\nb0lCQVFDN3JDZmdDT2dzMDI5NnJ1bENROWQrQ1BNcmZEdWhuZjRNDQpnQjN2YTB5RUw3OXEvVnVO\r\nTmc4YXptWkVkQTJiME5BWEpMbzB5K1hHY2xrZVpvTTdXaEE0aWMwOUlONiszTVVFenYveXlndTdv\r\nRGhmDQpnanRwbFBoQUtXT0JxWmtPSlFadzdkekZwbHJLOGtuY21WR1EvMS9JaGtpdllLbG5tdW5F\r\nSmh5Y2Y1UTdRZWdqbno3RFZIU3V5TGdiDQp3TWczOWY2d3BvbENacnNrdThwVVB2MVZvclcxb1JH\r\nVlBibkF3VUdNemtkK1pIRHhUa0JWM1NCUnVySWtsL2tUNjdoUmZVb1V0TjFwDQpVTTJmSTJrZkJJ\r\nMzU1L0JzSXRjMVl6cWZEZk8zaHNsS1ZCOWNTL0RYeXFTdEQ5MW5xOHZBUW96QmoxaVNIN0pGOXVQ\r\nYUVZWXFIaDEvDQpWcDgvQWdNQkFBR2pnZ0p4TUlJQ2JUQWRCZ05WSFE0RUZnUVV1Z3R3NzBqRHVs\r\nY2xsMy9Wa2ZhVkFxV1NDVjh3SHdZRFZSMGpCQmd3DQpGb0FVUXFhMTM1dTZ3Y2hubmwrbmV0aStH\r\nblUvZE8wd2dnRUxCZ05WSFI4RWdnRUNNSUgvTUlIOG9JSDVvSUgyaGxCb2RIUndPaTh2DQpZMlZ5\r\nZEdSaGRHRXhMbkIzWTJsdWRHVnlibUZzTG1OdmJTOURaWEowUkdGMFlURXZVSEpwWTJWM1lYUmxj\r\nbWh2ZFhObFEyOXZjR1Z5DQpjeVV5TUVsemMzVnBibWN4TG1OeWJJWlFhSFIwY0RvdkwyTmxjblJr\r\nWVhSaE1pNXdkMk5wYm5SbGNtNWhiQzVqYjIwdlEyVnlkRVJoDQpkR0V5TDFCeWFXTmxkMkYwWlhK\r\nb2IzVnpaVU52YjNCbGNuTWxNakJKYzNOMWFXNW5NUzVqY215R1VHaDBkSEE2THk5alpYSjBaR0Yw\r\nDQpZVE11Y0hkamFXNTBaWEp1WVd3dVkyOXRMME5sY25SRVlYUmhNeTlRY21salpYZGhkR1Z5YUc5\r\nMWMyVkRiMjl3WlhKekpUSXdTWE56DQpkV2x1WnpFdVkzSnNNSUlCR2dZSUt3WUJCUVVIQVFFRWdn\r\nRU1NSUlCQ0RDQmdRWUlLd1lCQlFVSE1BS0dkV2gwZEhBNkx5OWpaWEowDQpaR0YwWVRFdWNIZGph\r\nVzUwWlhKdVlXd3VZMjl0TDBObGNuUkVZWFJoTVM5MWMzUndZVE5uZEhOallUQXpMbTVoYlM1d2Qy\r\nTnBiblJsDQpjbTVoYkM1amIyMWZVSEpwWTJWM1lYUmxjbWh2ZFhObFEyOXZjR1Z5Y3lVeU1FbHpj\r\nM1ZwYm1jeEtETXBMbU55ZERDQmdRWUlLd1lCDQpCUVVITUFLR2RXaDBkSEE2THk5alpYSjBaR0Yw\r\nWVRJdWNIZGphVzUwWlhKdVlXd3VZMjl0TDBObGNuUkVZWFJoTWk5MWMzUndZVE5uDQpkSE5qWVRB\r\nekxtNWhiUzV3ZDJOcGJuUmxjbTVoYkM1amIyMWZVSEpwWTJWM1lYUmxjbWh2ZFhObFEyOXZjR1Z5\r\nY3lVeU1FbHpjM1ZwDQpibWN4S0RNcExtTnlkREFOQmdrcWhraUc5dzBCQVFVRkFBT0NBUUVBcWZo\r\nd2RjN0ZKb2NkTmw5SXpNOFdtTndIZDhGS1F2ZjVKczRZDQpER21jMDh2ZDRLYjRzWW9NL01Uc1Nw\r\nbEFMczRIK3dySWZMbVFIdXBmWlRFMmVDNXRSa0F3dVpWR0I0R0o5a0cwK1E5djNZMzZvNTd3DQo2\r\naGt6QlVGSitKaWJzc2tUNUpNTXZaVnh6SG10K3Z5dmtablBWa2tKWkp1RHA3TW8rZ0daTXY4aTRV\r\nVitHWnlZQWdrS1BDUXR3dVFKDQp5aE90MG1BUDhDb1dsVys1Q2dZVjJzWDRCbUVuZW9LY1JtZXlS\r\nNDlTQ2FEOFFRWmVlcitIQ2NEenJsOWdQOGlkN1NnRTRxL3pNbUd4DQpCRW1HTUQyV0kydzJ6Nnp1\r\nd3QyUktFWnl4QUJka2V2VDk4WG5tQTI0Z0RMVXZsYzNXd2lFd2xkYW0vaVR4VW5rV0hDR2x6WnhG\r\nWXUyDQpTQT09DQo8L2RzOlg1MDlDZXJ0aWZpY2F0ZT4NCjwvZHM6WDUwOURhdGE+DQo8L2RzOktl\r\neUluZm8+DQo8L2RzOlNpZ25hdHVyZT4NCiAgICAgICAgPG5zMjpTdWJqZWN0Pg0KICAgICAgICAg\r\nICAgPG5zMjpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlk\r\nLWZvcm1hdDp1bnNwZWNpZmllZCI+MTAwMDE2OTA5MTwvbnMyOk5hbWVJRD4NCiAgICAgICAgICAg\r\nIDxuczI6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1M\r\nOjIuMDpjbTpiZWFyZXIiPg0KICAgICAgICAgICAgICAgIDxuczI6U3ViamVjdENvbmZpcm1hdGlv\r\nbkRhdGEgSW5SZXNwb25zZVRvPSJfNGFmOGFkZDBlNzZhMjA3MjY2ODQiIE5vdE9uT3JBZnRlcj0i\r\nMjAxNC0wOC0xMlQwMzoyMDo1MVoiIFJlY2lwaWVudD0iaHR0cHM6Ly9wd2N0ZXN0LmF1dGgwLmNv\r\nbS9sb2dpbi9jYWxsYmFjaz9jb25uZWN0aW9uPVNpdGVtaW5kZXJEZXYiLz4NCiAgICAgICAgICAg\r\nIDwvbnMyOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgICAgIDwvbnMyOlN1YmplY3Q+DQogICAg\r\nICAgIDxuczI6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDgtMTJUMDM6MTc6NTFaIiBOb3RP\r\nbk9yQWZ0ZXI9IjIwMTQtMDgtMTJUMDM6MjA6NTFaIj4NCiAgICAgICAgICAgIDxuczI6QXVkaWVu\r\nY2VSZXN0cmljdGlvbj4NCiAgICAgICAgICAgICAgICA8bnMyOkF1ZGllbmNlPnVybjphdXRoMDpw\r\nd2N0ZXN0OlNpdGVtaW5kZXJEZXY8L25zMjpBdWRpZW5jZT4NCiAgICAgICAgICAgIDwvbnMyOkF1\r\nZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDwvbnMyOkNvbmRpdGlvbnM+DQogICAgICAgIDxu\r\nczI6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA4LTExVDIzOjI4OjI1WiIgU2Vz\r\nc2lvbkluZGV4PSJtKzVRKzNYbFVzUUFROURZMldocHFoRWFMNWM9YXd5OGZRPT0iIFNlc3Npb25O\r\nb3RPbk9yQWZ0ZXI9IjIwMTQtMDgtMTJUMDM6MjA6NTFaIj4NCiAgICAgICAgICAgIDxuczI6QXV0\r\naG5Db250ZXh0Pg0KICAgICAgICAgICAgICAgIDxuczI6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJu\r\nOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L25zMjpBdXRobkNv\r\nbnRleHRDbGFzc1JlZj4NCiAgICAgICAgICAgIDwvbnMyOkF1dGhuQ29udGV4dD4NCiAgICAgICAg\r\nPC9uczI6QXV0aG5TdGF0ZW1lbnQ+DQogICAgICAgIDxuczI6QXR0cmlidXRlU3RhdGVtZW50Pg0K\r\nICAgICAgICAgICAgPG5zMjpBdHRyaWJ1dGUgTmFtZT0iZm5hbWUiIE5hbWVGb3JtYXQ9InVybjpv\r\nYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dW5zcGVjaWZpZWQiPg0KICAg\r\nICAgICAgICAgICAgIDxuczI6QXR0cmlidXRlVmFsdWU+UHVzaHA8L25zMjpBdHRyaWJ1dGVWYWx1\r\nZT4NCiAgICAgICAgICAgIDwvbnMyOkF0dHJpYnV0ZT4NCiAgICAgICAgICAgIDxuczI6QXR0cmli\r\ndXRlIE5hbWU9ImxuYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6\r\nYXR0cm5hbWUtZm9ybWF0OnVuc3BlY2lmaWVkIj4NCiAgICAgICAgICAgICAgICA8bnMyOkF0dHJp\r\nYnV0ZVZhbHVlPkFicm9sPC9uczI6QXR0cmlidXRlVmFsdWU+DQogICAgICAgICAgICA8L25zMjpB\r\ndHRyaWJ1dGU+DQogICAgICAgICAgICA8bnMyOkF0dHJpYnV0ZSBOYW1lPSJlbWFpbCIgTmFtZUZv\r\ncm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1bnNwZWNp\r\nZmllZCI+DQogICAgICAgICAgICAgICAgPG5zMjpBdHRyaWJ1dGVWYWx1ZT5wdXNocC5hYnJvbEB1\r\ncy5wd2MuY29tPC9uczI6QXR0cmlidXRlVmFsdWU+DQogICAgICAgICAgICA8L25zMjpBdHRyaWJ1\r\ndGU+DQogICAgICAgICAgICA8bnMyOkF0dHJpYnV0ZSBOYW1lPSJwd2NndWlkIiBOYW1lRm9ybWF0\r\nPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVuc3BlY2lmaWVk\r\nIj4NCiAgICAgICAgICAgICAgICA8bnMyOkF0dHJpYnV0ZVZhbHVlPnBhYnJvbDAwMTwvbnMyOkF0\r\ndHJpYnV0ZVZhbHVlPg0KICAgICAgICAgICAgPC9uczI6QXR0cmlidXRlPg0KICAgICAgICA8L25z\r\nMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgPC9uczI6QXNzZXJ0aW9uPg0KPC9SZXNwb25zZT4=' + var samlOptions = { + thumbprints: ['6BF18C1DE16D8A6C7B79A0997EF96DEEF90CBF98'], + realm: 'urn:auth0:pwctest:SiteminderDev', + checkExpiration: false, + recipientUrl: 'https://pwctest.auth0.com/login/callback?connection=SiteminderDev' + }; + + var samlpOptions = { + protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + destinationUrl: 'https://pwctest.auth0.com/login/callback?connection=SiteminderDev' + }; + + var sm = new SamlPassport(samlOptions); + var sp = new samlp(samlpOptions, sm); + + sp.validateSamlResponse(new Buffer(response, 'base64').toString(), + function(err, profile){ + if (err) return done(err); + assert.ok(profile); + expect(profile['fname']).to.equal('Pushp'); + done(); + }); + + }); + + it('should throw an exception when validating an assertion from RSA IDM with no embedded signature and supplying only thumbrints', function (done) { + var response = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIENvbnNlbnQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjb25zZW50OnVuc3BlY2lmaWVkIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9uZXRmb3JteC5hdXRoMC5jb20vbG9naW4vY2FsbGJhY2s/Y29ubmVjdGlvbj1lbWMtdGVzdCIgSUQ9ImVkNmIxZDg4NDk0OTJjOTZiN2U1MmU0OGI1N2YxZGU0IiBJblJlc3BvbnNlVG89Il82ODhjNzJjYzBlMWE1ZGZjN2M3NiIgSXNzdWVJbnN0YW50PSIyMDE1LTA1LTI3VDExOjM1OjQxWiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2VtYy5jb20vaWRwL05ldGZvcm14PC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KPGRzOlNpZ25lZEluZm8+CjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIj48L2RzOkNhbm9uaWNhbGl6YXRpb25NZXRob2Q+CjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiPjwvZHM6U2lnbmF0dXJlTWV0aG9kPgo8ZHM6UmVmZXJlbmNlIFVSST0iI2VkNmIxZDg4NDk0OTJjOTZiN2U1MmU0OGI1N2YxZGU0Ij4KPGRzOlRyYW5zZm9ybXM+CjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSI+PC9kczpUcmFuc2Zvcm0+CjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiPjwvZHM6VHJhbnNmb3JtPgo8L2RzOlRyYW5zZm9ybXM+CjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSI+PC9kczpEaWdlc3RNZXRob2Q+CjxkczpEaWdlc3RWYWx1ZT5iYUtkS2hwQlZhT1lHSS8vVkNEU1N5b2JoY2s9PC9kczpEaWdlc3RWYWx1ZT4KPC9kczpSZWZlcmVuY2U+CjwvZHM6U2lnbmVkSW5mbz4KPGRzOlNpZ25hdHVyZVZhbHVlPgpGLzgrZUJpbWwrWWJqNmM1VERvaDNmYTY3Z01xRGlUV3hnamIrNmtjQTk2RDczcUdKSWhQWjhKZlZlM0paTHpQbUVUbm9oN0diWStyCjNFR3JqZk50bmJiRVdvYi9TUC9QQkxUOWhkeDdCR3FiT1dlTVlkTjYyWCs3OWFMZHd6QitNNkR2dXIreWlqS1NjVHhtdHNkeko4QXMKclRKN1pSTEZNMXcxR09qUzFQdEo3N2wxeENEWU9hb0VwSVZ1SlByRjZmNmVEUWJSTldVdUE5TVQ5d3BrYlhZbHYrTnFCWkswenZUTgpBK1Myc1AzUXhYenozVU9OeVlHdENPZEIrZzFybTgzNklNMHp4bUVMMys3QUdjNHBUVlJpRU5Sb2w2dDFFbGxxWVp5d1M3K2RoMGI0CnRXcGVhS2JTU2RKY1lGTnI2WUpsamZJUG9LZy9oelFKejdhSDBRPT0KPC9kczpTaWduYXR1cmVWYWx1ZT4KPC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIj48L3NhbWxwOlN0YXR1c0NvZGU+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIElEPSJlOTFhNzE1NTUzZWQ2NWFjY2RiMGNkYzVjYWVmMzk3YSIgSXNzdWVJbnN0YW50PSIyMDE1LTA1LTI3VDExOjM1OjQxWiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2VtYy5jb20vaWRwL05ldGZvcm14PC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OnVuc3BlY2lmaWVkIj43OTM1Mjwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iXzY4OGM3MmNjMGUxYTVkZmM3Yzc2IiBOb3RPbk9yQWZ0ZXI9IjIwMTUtMDUtMjdUMTE6MzY6NDFaIiBSZWNpcGllbnQ9Imh0dHBzOi8vbmV0Zm9ybXguYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249ZW1jLXRlc3QiPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YT48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNS0wNS0yN1QxMTozNToxMVoiIE5vdE9uT3JBZnRlcj0iMjAxNS0wNS0yN1QxMTozNjo0MVoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+dXJuOmF1dGgwOm5ldGZvcm14OmVtYy10ZXN0PC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNS0wNS0yN1QxMTozNDoxNloiIFNlc3Npb25JbmRleD0iZTkxYTcxNTU1M2VkNjVhY2NkYjBjZGM1Y2FlZjM5N2EiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpLZXJiZXJvczwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIj48c2FtbDpBdHRyaWJ1dGUgRnJpZW5kbHlOYW1lPSJ1c2VyX2lkIiBOYW1lPSJ1c2VyX2lkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj43OTM1Mjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBGcmllbmRseU5hbWU9ImVtYWlsIiBOYW1lPSJlbWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+cmlja3kuYnJvd25AZW1jLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==' + var samlOptions = { + thumbprints: ['bc58b95946e0c96b464b561b02d740aeae88875a'], + realm: 'urn:auth0:netformx:emc-test', + checkExpiration: false + }; + + var samlpOptions = { + protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + destinationUrl: 'https://fmi-test.auth0.com/login/callback', + }; + + var sm = new SamlPassport(samlOptions); + var sp = new samlp(samlpOptions, sm); + + sp.validateSamlResponse(new Buffer(response, 'base64').toString(), + function(err, profile){ + if (err) { + assert.ok(err); + return done(); + } + done('error expected'); + }); + }); + + it('should validate an assertion from RSA IDM with no embedded signature and supplying a cert', function (done) { + var response = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIENvbnNlbnQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjb25zZW50OnVuc3BlY2lmaWVkIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9uZXRmb3JteC5hdXRoMC5jb20vbG9naW4vY2FsbGJhY2s/Y29ubmVjdGlvbj1lbWMtdGVzdCIgSUQ9ImVkNmIxZDg4NDk0OTJjOTZiN2U1MmU0OGI1N2YxZGU0IiBJblJlc3BvbnNlVG89Il82ODhjNzJjYzBlMWE1ZGZjN2M3NiIgSXNzdWVJbnN0YW50PSIyMDE1LTA1LTI3VDExOjM1OjQxWiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2VtYy5jb20vaWRwL05ldGZvcm14PC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KPGRzOlNpZ25lZEluZm8+CjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIj48L2RzOkNhbm9uaWNhbGl6YXRpb25NZXRob2Q+CjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiPjwvZHM6U2lnbmF0dXJlTWV0aG9kPgo8ZHM6UmVmZXJlbmNlIFVSST0iI2VkNmIxZDg4NDk0OTJjOTZiN2U1MmU0OGI1N2YxZGU0Ij4KPGRzOlRyYW5zZm9ybXM+CjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSI+PC9kczpUcmFuc2Zvcm0+CjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiPjwvZHM6VHJhbnNmb3JtPgo8L2RzOlRyYW5zZm9ybXM+CjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSI+PC9kczpEaWdlc3RNZXRob2Q+CjxkczpEaWdlc3RWYWx1ZT5iYUtkS2hwQlZhT1lHSS8vVkNEU1N5b2JoY2s9PC9kczpEaWdlc3RWYWx1ZT4KPC9kczpSZWZlcmVuY2U+CjwvZHM6U2lnbmVkSW5mbz4KPGRzOlNpZ25hdHVyZVZhbHVlPgpGLzgrZUJpbWwrWWJqNmM1VERvaDNmYTY3Z01xRGlUV3hnamIrNmtjQTk2RDczcUdKSWhQWjhKZlZlM0paTHpQbUVUbm9oN0diWStyCjNFR3JqZk50bmJiRVdvYi9TUC9QQkxUOWhkeDdCR3FiT1dlTVlkTjYyWCs3OWFMZHd6QitNNkR2dXIreWlqS1NjVHhtdHNkeko4QXMKclRKN1pSTEZNMXcxR09qUzFQdEo3N2wxeENEWU9hb0VwSVZ1SlByRjZmNmVEUWJSTldVdUE5TVQ5d3BrYlhZbHYrTnFCWkswenZUTgpBK1Myc1AzUXhYenozVU9OeVlHdENPZEIrZzFybTgzNklNMHp4bUVMMys3QUdjNHBUVlJpRU5Sb2w2dDFFbGxxWVp5d1M3K2RoMGI0CnRXcGVhS2JTU2RKY1lGTnI2WUpsamZJUG9LZy9oelFKejdhSDBRPT0KPC9kczpTaWduYXR1cmVWYWx1ZT4KPC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIj48L3NhbWxwOlN0YXR1c0NvZGU+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIElEPSJlOTFhNzE1NTUzZWQ2NWFjY2RiMGNkYzVjYWVmMzk3YSIgSXNzdWVJbnN0YW50PSIyMDE1LTA1LTI3VDExOjM1OjQxWiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2VtYy5jb20vaWRwL05ldGZvcm14PC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OnVuc3BlY2lmaWVkIj43OTM1Mjwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iXzY4OGM3MmNjMGUxYTVkZmM3Yzc2IiBOb3RPbk9yQWZ0ZXI9IjIwMTUtMDUtMjdUMTE6MzY6NDFaIiBSZWNpcGllbnQ9Imh0dHBzOi8vbmV0Zm9ybXguYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249ZW1jLXRlc3QiPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YT48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNS0wNS0yN1QxMTozNToxMVoiIE5vdE9uT3JBZnRlcj0iMjAxNS0wNS0yN1QxMTozNjo0MVoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+dXJuOmF1dGgwOm5ldGZvcm14OmVtYy10ZXN0PC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNS0wNS0yN1QxMTozNDoxNloiIFNlc3Npb25JbmRleD0iZTkxYTcxNTU1M2VkNjVhY2NkYjBjZGM1Y2FlZjM5N2EiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpLZXJiZXJvczwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIj48c2FtbDpBdHRyaWJ1dGUgRnJpZW5kbHlOYW1lPSJ1c2VyX2lkIiBOYW1lPSJ1c2VyX2lkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj43OTM1Mjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBGcmllbmRseU5hbWU9ImVtYWlsIiBOYW1lPSJlbWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+cmlja3kuYnJvd25AZW1jLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg=='; + var encoded_cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tIApNSUlGVERDQ0JEU2dBd0lCQWdJUkFOYjZhMktpVzIzS3d1TFZWYjlyamRjd0RRWUpLb1pJaHZjTkFRRUZCUUF3CmdaNHhDekFKQmdOVkJBWVRBbFZUTVJZd0ZBWURWUVFJRXcxTllYTnpZV05vZFhObGRIUnpNUkF3RGdZRFZRUUgKRXdkQ1pXUm1iM0prTVJrd0Z3WURWUVFLRXhCU1UwRWdVMlZqZFhKcGRIa2dURXhETVNVd0l3WURWUVFMRXh4SApiRzlpWVd3Z1UyVmpkWEpwZEhrZ1QzSm5ZVzVwZW1GMGFXOXVNU013SVFZRFZRUURFeHBTVTBFZ1EyOXljRzl5CllYUmxJRk5sY25abGNpQkRRU0IyTWpBZUZ3MHhOREF4TVRjeE9USXpNVE5hRncweE5qQXhNVGN4T1RJek1UTmEKTUlHYU1Rc3dDUVlEVlFRR0V3SlZVekVXTUJRR0ExVUVDQk1OVFdGemMyRmphSFZ6WlhSMGN6RVNNQkFHQTFVRQpCeE1KU0c5d2EybHVkRzl1TVJnd0ZnWURWUVFLRXc5RlRVTWdRMjl5Y0c5eVlYUnBiMjR4SnpBbEJnTlZCQXNUCkhrVnVkR1Z5Y0hKcGMyVWdWR1ZqYUc1dmJHOW5lU0JUWlhKMmFXTmxjekVjTUJvR0ExVUVBeE1UWm1sdGRITjAKTG1semRYTXVaVzFqTG1OdmJUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU9pSApodmpiSXg0TEhlSHZGOFRzMDRMa3hlRm1abFRRNHRVQjVvSm05ajE5aU92aDdXZ2U2cE0yWEQrejhRWlFXK2NUCm4vTGtBUDM1djRzVGt1Z3BPVExmOHc5ekJESXpTQ01BOWVqeisvS0p6enhFVUtaZVo0TU1jbldUeGpXS05zR0cKOXVtcjUxTVQ3THU4aFgwNUIyMFBrbVB5Zkt4ZExDQVJqZUhGWlBQZVF3NGd0VDVDbmJIUW0yRlE5SFJEWFhGdwo2V1lmVXRGTXRnNEl1LzJxbTF6eWpBejNjTjN1K1FXcXlZUG1rRzR2TFU5L2xzclpPYXN6TU84czBrQWo3Z2tPCkV2akdXaW1RRTB2N2F5M0xpYzcrbGZMRURLVVR6R2pvQkJnc1dEc1BZWHF0OTA1RGx1UDJMRzNLVmMvc0hLRmIKN2V5VUp1T0FaMHpDM2Q0Y2tYa0NBd0VBQWFPQ0FZVXdnZ0dCTUE0R0ExVWREd0VCL3dRRUF3SUR1REFSQmdsZwpoa2dCaHZoQ0FRRUVCQU1DQnNBd0hnWURWUjBSQkJjd0ZZSVRabWx0ZEhOMExtbHpkWE11WlcxakxtTnZiVEFmCkJnTlZIU01FR0RBV2dCUXA4OEpqMC9IMUM2Vy9zbDNmWVJDVU5NOUhERENCa1FZRFZSMGdCSUdKTUlHR01JR0QKQmdrcWhraUc5dzBGQndVd2RqQXVCZ2dyQmdFRkJRY0NBUllpYUhSMGNEb3ZMMk5oTG5KellYTmxZM1Z5YVhSNQpMbU52YlM5RFVGTXVhSFJ0YkRCRUJnZ3JCZ0VGQlFjQ0FqQTRNQmNXRUZKVFFTQlRaV04xY21sMGVTQk1URU13CkF3SUJBUm9kUTFCVElFbHVZMjl5Y0c5eVlYUmxaQ0JpZVNCeVpXWmxjbVZ1WTJVd0hRWURWUjBsQkJZd0ZBWUkKS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUIwR0ExVWREZ1FXQkJUdFdoSnE5S3VsMXpNbHIrOHVSbldheitRVQpZekJKQmdOVkhSOEVRakJBTUQ2Z1BLQTZoamhvZEhSd09pOHZZM0pzTG5KellYTmxZM1Z5YVhSNUxtTnZiVG80Ck1DOVNVMEZEYjNKd2IzSmhkR1ZUWlhKMlpYSkRRWFl5TG1OeWJEQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUEKTWFidGJCNXIwWU5lcTByRitsdmZKc3dMVTFZU2xxOHNaTkJVbzZDNWJIOFN3c01yeXJxUldZcUhoMWdkb21SUApGNWpYanFIWlE3UG9LcW9Ca1NRbU9kNERxdHp0NTlmUHp3MG5nRU9MdFN4M1Z2QldXU21LYTJwdDZ4a21PbGFxCi9nVTdzZElacTBjeGlSbERuaUZDQVp5dERtWWo2T1lndlIrQlN5WmdNM3VnaXltQlNlUjQ2M2owbkZ3bDIrM28KRXRFUnpTdW9paGRtYzhkV09TaU0zSWtja25wUzh4cnpRUjd5NVZkbnBoa2Q5K09rekt1UFM4WVNBYkw3WFl5OQpZWXYwR3lJdVBVa0VhL3FRNnlGR0k2NU5PR3ZDZWlGRjRvc2N0NmxVSFM5THZNZG1jeFl5cVg3UEhkUnh1ZHJFCkZDNWhCT2tZclNidVNKNUpGTEdPM0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=='; + var cert = pemToCert(new Buffer(encoded_cert, 'base64').toString()); + var samlOptions = { + cert: cert, + thumbprints: ['bc58b95946e0c96b464b561b02d740aeae88875a'], + realm: 'urn:auth0:netformx:emc-test', + checkExpiration: false, + recipientUrl: 'https://netformx.auth0.com/login/callback?connection=emc-test' + }; + + var samlpOptions = { + cert: cert, + destinationUrl: 'https://netformx.auth0.com/login/callback?connection=emc-test', + }; + + var sm = new SamlPassport(samlOptions); + var sp = new samlp(samlpOptions, sm); + + sp.validateSamlResponse(new Buffer(response, 'base64').toString(), + function(err, profile){ + if (err) return done(err); + assert.ok(profile); + expect(profile['email']).to.equal('ricky.brown@emc.com'); + done(); + }); + + function pemToCert(pem) { + var cert = /-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----/g.exec(pem.toString()); + if (cert.length > 0) { + return cert[1].replace(/[\n|\r\n]/g, ''); + } + return null; + } + }); + + it('should validate thumbprint always for embedded signatures (even if cert is passed)', function (done) { + var response = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfZTU3YjcwNmU3ZWU0YWE4Y2Y2MjQiICBJblJlc3BvbnNlVG89Il9jMTQwZDE1ZjU2Y2Q1MzBkMzEzOCIgIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE2LTAyLTI0VDIxOjEyOjAwWiIgIERlc3RpbmF0aW9uPSJodHRwczovL3dwdGVzdC5hdXRoMC5jb20vbG9naW4vY2FsbGJhY2s/Y29ubmVjdGlvbj1QZXBlU0FNTCI+PHNhbWw6SXNzdWVyIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPnVybjphdXRoMC1waHAuYXV0aDAuY29tPC9zYW1sOklzc3Vlcj48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpBc3NlcnRpb24geG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgVmVyc2lvbj0iMi4wIiBJRD0iX2ptTjcwOGRIVEZSd1JLenR3aGtrNGFnNkFJdG9NNDYxIiBJc3N1ZUluc3RhbnQ9IjIwMTYtMDItMjRUMjE6MTI6MDAuMjI5WiI+PHNhbWw6SXNzdWVyPnVybjphdXRoMC1waHAuYXV0aDAuY29tPC9zYW1sOklzc3Vlcj48U2lnbmF0dXJlIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48U2lnbmVkSW5mbz48Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz48UmVmZXJlbmNlIFVSST0iI19qbU43MDhkSFRGUndSS3p0d2hrazRhZzZBSXRvTTQ2MSI+PFRyYW5zZm9ybXM+PFRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PFRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvVHJhbnNmb3Jtcz48RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48RGlnZXN0VmFsdWU+UDgrNGMzeFNMbjFrelRSSUMzYUJ4OFJFSndJPTwvRGlnZXN0VmFsdWU+PC9SZWZlcmVuY2U+PC9TaWduZWRJbmZvPjxTaWduYXR1cmVWYWx1ZT5TZWtjRThaZnFsbVhlNHBEY05pbUI3MGRxQzF1SW5HZ1I2S2NsUzdCNGFvNFBERVlwclhiSVNPZ0E0bjlOVTdreTRnNzBxeitUcDQ2dmU5MU9OTThCM2tXR0ErQXNiUjRZNDBBRjZqaGgvRWhrcVhRKzV6QVlhajBoNEpKYm1Xb2JYdW9xNlpIZW0zZDdveWxZaWV0R2FTOUl6V3Y0MmIyQ3p1eXZCTkxIZk4vZUtqOVRROFBrUDBsSi9seTZrVHBVL25OYTZOR0NxbEJFb2dHeDl0TXY0Sjl4UG9uYXpQYXB6elJlQVh2dTY0Y3dGa2VXYU8ybUhoaitZT3lHVUhtRWpMbHVwOGhnTFhhcTdST1p1czVEL2ZxS0t5bU9BdWR2b01PazBuVktqeUpyTStnc3JORWZXdTJqWkx4ZitzSXlhcnRMU3Q5c2VzVW44RDY5OS9ENlE9PTwvU2lnbmF0dXJlVmFsdWU+PEtleUluZm8+PFg1MDlEYXRhPjxYNTA5Q2VydGlmaWNhdGU+TUlJQzdqQ0NBZGFnQXdJQkFnSUpUU3NsTkhLWG9ubGJNQTBHQ1NxR1NJYjNEUUVCQlFVQU1CNHhIREFhQmdOVkJBTVRFMkYxZEdnd0xYQm9jQzVoZFhSb01DNWpiMjB3SGhjTk1UWXdNVEUxTVRZMU5USTJXaGNOTWprd09USXpNVFkxTlRJMldqQWVNUnd3R2dZRFZRUURFeE5oZFhSb01DMXdhSEF1WVhWMGFEQXVZMjl0TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF0RlZ4Rm11OFpGYngyM2JONklmTTZia3VoUEUrbG1QTkYxSHBCR1JPSGh5Q1RrVDA5OXRTN1ZJNHVWZEdCT3BYN3NEUXkvYXpsU2FkQ3NQWlNnQ2ZTU3lEcUZIWWI0eDRjTTJOcU9RQzdhdTNwRVVMOEhKWnhneXZMYkV1dENDTkgwR25IYTRHZWx4cldvNTl1RlNpeUJxL3AzZ2ttY0c0UDJPeXRGMDJ0ekk5SWFUL2Q5SStTQkQrUDA1TGx4MktwYVErU3JOUGF0QUlxaEJTb1Q4UThGSm84U0hzN0hsYTZRWkhyWFBQcHUxUmVra1NpYStEZG51YjViMEF1Sm9JbzNKUzU2WTZGN05RTncyeFlFaFBLMjdUV2tBZzJsNFl3WEM5RDN6d0RuaUJJSkNaRm9tOVlQRlhITXhBcUx0NVNFaFJlOVZnS1VwOFk1K3pURUJ5c1FJREFRQUJveTh3TFRBTUJnTlZIUk1FQlRBREFRSC9NQjBHQTFVZERnUVdCQlE0Y1hmaXZjQkNPYi9wN3ZEdFhhMGE2blhZZlRBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQUNlUVRWdVlGbFVCOFVVMTVsOVhSR1p2cWlFTDlZNVN3M1lQWjVZREhBclVEWnNOM2pzRG9qN3ErQjVBQTlOSWpHMlQ0cUJVZ3hzTHJPV2lPaTRYUnV0cHlZTzZRMnZ1N3JKYlNUcThZZGxaYm52cjhURHZDT3ZveXJPeE94WXRIWlEzTEV0aWlVazFPVWhLU0VzNWJsRU13ZXk2dFAzWDNsRWRRVk9tSTJoc1Jla00wWi93Z3lZZGliM2tuU2tETDhyVlBraVFITmhWS3hpK3pvd09Uc09ZTDRHYUFvNUZSQ1dEcjZJaGlHM2tsM2ZvOHpKd1d4dlJyaklQMmJ5RmtXdTBLS2owdkNYTVYvdGdwZ1lHOE5QakVzb3hxNlAwMUhXamxwd1lWSi84YVpsK2YxS21kTUJoalMrTzU0a1JBNXFWMkc0UWFmRFFlQmIvN2UyWEJKQT09PC9YNTA5Q2VydGlmaWNhdGU+PC9YNTA5RGF0YT48L0tleUluZm8+PC9TaWduYXR1cmU+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDp1bnNwZWNpZmllZCI+YXV0aDB8NTZiMTEwYjhkOWQzMjdlNzA1ZTFkMmRhPC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDE2LTAyLTI0VDIyOjEyOjAwLjIyOVoiIFJlY2lwaWVudD0iaHR0cHM6Ly93cHRlc3QuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249UGVwZVNBTUwiIEluUmVzcG9uc2VUbz0iX2MxNDBkMTVmNTZjZDUzMGQzMTM4Ii8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTYtMDItMjRUMjE6MTI6MDAuMjI5WiIgTm90T25PckFmdGVyPSIyMDE2LTAyLTI0VDIyOjEyOjAwLjIyOVoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+dXJuOmF1dGgwOndwdGVzdDpQZXBlU0FNTDwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIj48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5hdXRoMHw1NmIxMTBiOGQ5ZDMyN2U3MDVlMWQyZGE8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZW1haWxhZGRyZXNzIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+Z2VybWFuLmxlbmFAZ21haWwuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5nZXJtYW4ubGVuYUBnbWFpbC5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvdXBuIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+Z2VybWFuLmxlbmFAZ21haWwuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLmF1dGgwLmNvbS9pZGVudGl0aWVzL2RlZmF1bHQvcHJvdmlkZXIiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5hdXRoMDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy5hdXRoMC5jb20vaWRlbnRpdGllcy9kZWZhdWx0L2Nvbm5lY3Rpb24iPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5Vc2VybmFtZS1QYXNzd29yZC1BdXRoZW50aWNhdGlvbjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy5hdXRoMC5jb20vaWRlbnRpdGllcy9kZWZhdWx0L2lzU29jaWFsIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+ZmFsc2U8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMuYXV0aDAuY29tL2VtYWlsX3ZlcmlmaWVkIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+ZmFsc2U8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMuYXV0aDAuY29tL2NsaWVudElEIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+U3prUHh4QUdEMXdmek5PYTNYWWlnMjVaWkhMbWhZd1g8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMuYXV0aDAuY29tL3VwZGF0ZWRfYXQiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5XZWQgRmViIDI0IDIwMTYgMjE6MTI6MDAgR01UKzAwMDAgKFVUQyk8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMuYXV0aDAuY29tL3BpY3R1cmUiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5odHRwczovL3MuZ3JhdmF0YXIuY29tL2F2YXRhci8wMDMxZDg1Yjg0MWViOTYwMWFmMDZiY2MyOTc4Y2NmMj9zPTQ4MCZhbXA7cj1wZyZhbXA7ZD1odHRwcyUzQSUyRiUyRmNkbi5hdXRoMC5jb20lMkZhdmF0YXJzJTJGZ2UucG5nPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLmF1dGgwLmNvbS9uaWNrbmFtZSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOmFueVR5cGUiPmdlcm1hbi5sZW5hPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLmF1dGgwLmNvbS9pZGVudGl0aWVzIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+W29iamVjdCBPYmplY3RdPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLmF1dGgwLmNvbS9jcmVhdGVkX2F0Ij48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+VHVlIEZlYiAwMiAyMDE2IDIwOjI1OjI4IEdNVCswMDAwIChVVEMpPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTYtMDItMjRUMjE6MTI6MDAuMjI5WiIgU2Vzc2lvbkluZGV4PSJfRzc3b1BnaVlyQ3Jpem1od3hPTkRjd0NNNFB6NEM3SEUiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3Nlczp1bnNwZWNpZmllZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+'; + var cert = 'MIIDDzCCAfegAwIBAgIJAP4bcs4flyQjMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNVBAMME2dsZW5hZGVtby5hdXRoMC5jb20wHhcNMTUwNjE3MTUzMjI0WhcNMjkwMjIzMTUzMjI0WjAeMRwwGgYDVQQDDBNnbGVuYWRlbW8uYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbeKIeZMg8V1AgNrd8ufEmMzZbDa+olq9ftUlDlpdzrt5AB7GJ87v7tNIP1EYiqNZ+2Jlat660Tz6omC3SCz3lzaugRvim4/ZrltNjSgtOmIAlIx0IfdGDeVMSJXv9P/iPECZ2iHsVl8n4G4gAwinwK4hbRe0D+LQJJUulUr9Bp4NeBKzxh3Ds7nXRBPhVMUPP/IZTr47mLGrJaQQvPy1rxfGTuUPPWtr9MckxtZ9oIndN0r1urlSNOetoGjcWOYC3KwBVaUW1gxr1Yrip9W2XxidO8r0AI1pPepJMc26EFznfFfADbubSXQilYxgOp8NNfjc6jESkaWxLUYa1QmpQIDAQABo1AwTjAdBgNVHQ4EFgQUS+AG8m9k8PSUPPzeDkzhhWJ7DpgwHwYDVR0jBBgwFoAUS+AG8m9k8PSUPPzeDkzhhWJ7DpgwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAIu0zdkl1kdmuJ6xOUEZDvE20epuHdoqvaJr9w4y7S8pIdvQ7qMZfPQbW5D6gr0Q6MkoycD5Nx6wS9W4iQ00JcYA//y8rB9mjsOPBXcjCVqk/icM6qDmki5RfAg32t4YsgkuxxWMbRghzMagT0NcoOZqscwXPV6E6dJc9xp/f0Gpka5laeFuHEyZQ8003P7prf+qYa333DS0Ws49vkXtHRgrYXsOoX0k2AiepIi4bt9CbxR/xKwcz8L0Qs+Aj1W+lUzKqerl3ctDrhls9/83yPFFyRBvhiqd2osh6H3Qb91n3Gdmcrh1dILWL7p2gQVxJ01wEWEMfYClmtPpA1ukUPg=='; + var samlOptions = { + cert: cert, + thumbprints: ['ANOTHER_THUMB'], + realm: 'urn:auth0:wptest:PepeSAML', + recipientUrl: 'https://fmi-test.auth0.com/login/callback', + destinationUrl: 'https://fmi-test.auth0.com/login/callback', + checkExpiration: false, + }; + + var samlpOptions = { + cert: cert, + checkDestination: false + }; + + var sm = new SamlPassport(samlOptions); + var sp = new samlp(samlpOptions, sm); + + sp.validateSamlResponse(new Buffer(response, 'base64').toString(), function (err){ + assert.ok(err); + expect(err.toString()).to.equal('Error: Invalid thumbprint (configured: ANOTHER_THUMB. calculated: CD78CA598A6FB28A4D70EF6846C1141666A24240)'); + done(); + }); + }); + + it('should validate an assertion from a WS-Fed STS using WS-Trust 1.3 namespaces', function (done) { + + var options = { + thumbprint: '1756139e2a046d3c494daae6bbfa542a4367bc60', + checkExpiration: false, + checkRecipient: false, + realm: 'http://dev.pms.baxon.net/' + }; + + var s = new wsfed(options, function(u,done){ + expect(u['email']).to.equal('fhermida@baxonpe.com'); + done(); + }); + + s.fail = function(e,code){ + done(e); + }; + + s.error = function(e){ + done(e); + }; + + var response = fs.readFileSync(__dirname + '/wsfed-result-wstrust13.xml').toString(); + + var req = { + get: function(){ + return '' + }, + body : { + wresult: response + } + }; + + s._authenticate_saml(req); + }); + + describe('wsfed fault response received', function () { + + var options = { + thumbprint: '1756139e2a046d3c494daae6bbfa542a4367bc60' + }; + + it('should extract soap fault with detail, code, subCode and message', function (done) { + var s = new wsfed(options, function(u,done){ + done('It should fail'); + }); + + s.fail = function(e,code){ + expect(e).to.have.property('name','AuthenticationFailedError'); + expect(e).to.have.property('message','User cancelled'); + expect(e).to.have.property('detail','USER_CANCEL'); + done(); + }; + + s.error = function(e){ + done(e); + }; + + var response = fs.readFileSync(__dirname + '/soap-fault.xml').toString(); + + var req = { + body : { + wresult: response + } + }; + + s._authenticate_saml(req); + }); + + it('should extract soap fault with empty information when there is no information (extreme, it should not happen)', function (done) { + var s = new wsfed(options, function(u,done){ + done('It should fail'); + }); + + s.fail = function(e,code){ + expect(e).to.have.property('name','AuthenticationFailedError'); + expect(e).to.have.property('message','Authentication Failed'); + expect(e).to.have.property('detail').and.to.be.undefined; + done(); + }; + + s.error = function(e){ + done(e); + }; + + var response = fs.readFileSync(__dirname + '/soap-fault-no-info.xml').toString(); + + var req = { + body : { + wresult: response + } + }; + + s._authenticate_saml(req); + }); + }); + + + // it('should validate a saml response from datapower', function (done) { + // var SAMLResponse = 'www.axa-equitable.comwww.axa-equitable.comChristopher.Owen@axa-advisors.com.datastageurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified3338532ChristopherOwenChristopher.Owen@axa-advisors.com.datastage7mSr1qvkKTrrV2bV+HFVJKpKaCI=BQTRkGg8M/g2LpkgvW3MQG/B0cDxsz0zHa2wejIN2010r+hS4BCj7YcIH319R+Y4oZTmlxmJIT/4wlR9rxHQWxX95jdB1DfeoUMLAlk2JfAT+ByZ14F+N4B7lDILuNUTrDBNa9GLDIo8MyAK8SBUdeyqDtNFqb44/gGg6B0h2qEbDNLHY7WlAxvz4TfndKqk5v/VP96xiCS4d1AjYPvUL8EzR5kS83ABHX0jg2bYxdtctdiKOilcHQOHEIKosWw86b9uDQPjIrSt1JzW8SSSeA6M3nJ7HJ/5EsSYEMvt/FshBnKP2LI4HMGktlzw/9gOnmxR/CQykMmN2vvCwPsnXA==MIIFQTCCBCmgAwIBAgIETB7lIzANBgkqhkiG9w0BAQUFADCBsTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9ycGEgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDkgRW50cnVzdCwgSW5jLjEuMCwGA1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxQzAeFw0xMzAxMzExNTM2MjBaFw0xNTAzMzAxOTA2MTJaMIGGMQswCQYDVQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMS0wKwYDVQQKEyRBWEEgRXF1aXRhYmxlIExpZmUgSW5zdXJhbmNlIENvbXBhbnkxIjAgBgNVBAMTGXJ0aWdpbnQuYXhhLWVxdWl0YWJsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCn6cGpvyzVSQ2c1oK7LzuxUXW4sxBOTGLVCqo4FWcta7QAU7RU5i3ATWxyo9HFmD8Qcyj6YuIQYtljJFAx/JcZigtXNRVudtl0uuvCUTGfsR67+gGRWe7hNg/9gIWLXZGkikRT4g9yBqutMzHeuX0ecignFHJxw5S7p1rtxJmuXQR/8uOeAse+48PkZcCHFdzBp6u3Z+pzite12LA2F8C0K5nv9FgkHVEQjqgtgAjKin2QmWqI1gj6mIe0oMxWB4l3j7dsEXVO4zPU1ujIylY0y2QnK4PbNdGu+W1GElwvUhSaz+jmIUFWklJtsFyhFVGcgFRE5iXXmrlnK4GZv3/FAgMBAAGjggGIMIIBhDALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50cnVzdC5uZXQvbGV2ZWwxYy5jcmwwZAYIKwYBBQUHAQEEWDBWMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAvBggrBgEFBQcwAoYjaHR0cDovL2FpYS5lbnRydXN0Lm5ldC8yMDQ4LWwxYy5jZXIwSgYDVR0gBEMwQTA1BgkqhkiG9n0HSwIwKDAmBggrBgEFBQcCARYaaHR0cDovL3d3dy5lbnRydXN0Lm5ldC9ycGEwCAYGZ4EMAQICMCQGA1UdEQQdMBuCGXJ0aWdpbnQuYXhhLWVxdWl0YWJsZS5jb20wHwYDVR0jBBgwFoAUHvGriQb4SQ8BM3fuFHruGXyTKE0wHQYDVR0OBBYEFJSL34z5hLkS08mEdEodVmeUrUC5MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADggEBAFCpXB2HagR+B4C5XKbtBPNZ3F94zJoFlCb+7/5Q9HWMGew1XiRx7GFhV4FCIsr6Gp9EYFLlVO+afdSMvNC7RauZ6nw7ylK8yRyuvbpJWC+XAfP4nVKroYYFPmKJdkELIBLIwt1Nsr3KsY0JIykokuQR/pWDSNVgo3arsNxXOhvxate0yoloMCUhHh+9b8WRY2JECN6fAEpg54pr5cjTCOCLFEN37M3Hl1+LRYNb6XAKUiL4b5CNkd/qUI5PZsJvGg/AOYRiCPY3iZezs9OT1RCkBrgM1W9rRF/zXCniRV3ASH22AU1jUn0OT1wy9B8PO15liIzw4WuYIdFqCxaIyJE=CN=Entrust Certification Authority - L1C, OU="(c) 2009 Entrust, Inc.", OU=www.entrust.net/rpa is incorporated by reference, O="Entrust, Inc.", C=US1277093155'; + // //var SAMLResponse = 'www.axa-equitable.comwww.axa-equitable.comChristopher.Owen@axa-advisors.com.datastageurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified3338532ChristopherOwenChristopher.Owen@axa-advisors.com.datastage'; + + // // var sig = new SignedXml(null, { signatureAlgorithm: 'http://www.w3.org/2000/09/xmldsig#rsa-sha1', idAttribute: 'ID' }); + // // sig.addReference("//*[local-name(.)='Response' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:protocol']", + // // ["http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#"], + // // 'http://www.w3.org/2000/09/xmldsig#sha1'); + + // // sig.signingKey = fs.readFileSync(__dirname + '/test-auth0.key'); + + // // sig.keyInfoProvider = { + // // getKeyInfo: function () { + // // return "" + fs.readFileSync(__dirname + '/test-auth0.cer') + ""; + // // } + // // }; + // // sig.computeSignature(SAMLResponse); + // // SAMLResponse = sig.getSignedXml(); + + // //console.log(SAMLResponse) + // // done(); + + // var saml_passport = new SamlPassport({thumbprints: ['ea410870b7699f08888b0b5ef0fbf11a6a157b4b'], + // realm: 'spn:408153f4-5960-43dc-9d4f-6b717d772c8d', + // validateResponse: true, + // checkExpiration: false}); // dont check expiration since we are harcoding the token + // var profile = saml_passport.validateSamlResponse(SAMLResponse, function(err, profile) { + // if (err) return done(err); + + // assert.ok(profile); + // done(); + // }); + + // }); +}); diff --git a/test/jwt.tests.js b/test/jwt.tests.js new file mode 100644 index 0000000..09d216f --- /dev/null +++ b/test/jwt.tests.js @@ -0,0 +1,45 @@ +var expect = require('chai').expect; +var jwt = require('jsonwebtoken'); +var fs = require('fs'); +var Strategy = require('../lib/passport-wsfed-saml2/strategy'); + +var cert = { + pub: fs.readFileSync(__dirname + '/test-auth0.pem'), + key: fs.readFileSync(__dirname + '/test-auth0.key') +}; + +var s = new Strategy({ + cert: cert.pub, + jwt: { + algorithm: 'RS256' + } +}, function (profile, done) { + done(null, profile); +}); + +describe('jwt support', function () { + + it('should work', function (done) { + s.success = function (user) { + expect(user.foo).to.equal('bar'); + done(); + }; + + s.fail = done; + s.error = done; + + var token = jwt.sign({ + foo: 'bar' + }, cert.key, { algorithm: 'RS256'}); + + var req = { + method: 'POST', + body: { + wresult: token + } + }; + + s.authenticate(req, {}); + }); + +}); \ No newline at end of file diff --git a/test/saml11.tests.js b/test/saml11.tests.js new file mode 100644 index 0000000..e56524d --- /dev/null +++ b/test/saml11.tests.js @@ -0,0 +1,127 @@ +var assert = require('assert'), + fs = require('fs'), + helpers = require('./helpers'), + saml11 = require('saml').Saml11, + SamlPassport = require('../lib/passport-wsfed-saml2/saml').SAML; + +describe('saml 1.1 assertion', function () { + + it('should parse attributes', function (done) { + // cert created with: + // openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem + + var options = { + cert: fs.readFileSync(__dirname + '/test-auth0.pem'), + key: fs.readFileSync(__dirname + '/test-auth0.key'), + issuer: 'urn:issuer', + lifetimeInSeconds: 600, + audiences: 'urn:myapp', + attributes: { + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'foo@bar.com', + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name': 'Foo Bar' + }, + nameIdentifier: 'foo', + nameIdentifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' + }; + + var signedAssertion = saml11.create(options); + + var publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + var saml_passport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient : false}); + saml_passport.validateSamlAssertion(signedAssertion, function(error, profile) { + + assert.ok(profile); + assert.equal('foo', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']); + assert.equal('Foo Bar', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name']); + assert.equal('foo@bar.com', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']); + assert.equal('foo@bar.com', profile['email']); + assert.equal('urn:issuer', profile['issuer']); + done(); + }); + + }); + + it('should handle unicode', function (done) { + + var options = { + cert: fs.readFileSync(__dirname + '/test-auth0.pem'), + key: fs.readFileSync(__dirname + '/test-auth0.key'), + issuer: 'urn:issuer', + lifetimeInSeconds: 600, + audiences: 'urn:myapp', + attributes: { + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'сообщить@bar.com', + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name': 'сообщить вКонтакте' + }, + nameIdentifier: 'вКонтакте', + nameIdentifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' + }; + + var signedAssertion = saml11.create(options); + + var publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + var saml_passport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient : false}); + var profile = saml_passport.validateSamlAssertion(signedAssertion, function(error, profile) { + if (error) return done(error); + + assert.ok(profile); + assert.equal('вКонтакте', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']); + assert.equal('сообщить вКонтакте', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name']); + assert.equal('сообщить@bar.com', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']); + done(); + }); + + }); + + it('should validate an assertion from office365', function (done) { + var signedAssertion = 'https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/TzJmLs0BTPgpaPLsA7L2Kd9l1k4IBOmwIM/znV2iOPU=OHJCAffCNPRkwsE3RqnVPoCRSqsPrio8prABauzu2pqF418Y1QJuJehhzztY8A6kwnBUkBVE7BIyLe7kgCnBoNZWElYki1xtaLksc/Afc0TjlZvv9IJ9fQHIBiL1JA9KcySq1tu9dv/NauykBODXuljPuVTk6I4xLLWcg20o26Ov57axp42uWPpcJHtasomLmmmnAXEh6P7aB/1Vlm/MAJhWXToxacauJzFao3F9JNEuucKY6y3RPDp1Qq3vL0gq98RKuiaejayu6RjyyU2+8vCBzURul8b7ZXPUHfIOME6Q5LvbKqLhe/mzqRc+9GUg22X3B5SYjdnXjwHbBTbihA==MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng10030000838D23AF@MicrosoftOnline.comspn:408153f4-5960-43dc-9d4f-6b717d772c8d75696069-df44-4310-9bcf-08b45e3007c9Matiasmatias@auth0.onmicrosoft.comWoloskihttps://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/urn:oasis:names:tc:SAML:2.0:ac:classes:Password'; + + var saml_passport = new SamlPassport({thumbprints: ['3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe0'], + realm: 'spn:408153f4-5960-43dc-9d4f-6b717d772c8d', + checkRecipient: false, + checkExpiration: false}); // dont check expiration since we are harcoding the token + var profile = saml_passport.validateSamlAssertion(signedAssertion, function(error, profile) { + + assert.ok(profile); + done(); + }); + + }); + + it('should return error if validation fails', function (done) { + var signedAssertion = 'https://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/TzJmLs0BTPgpaPLsA7L2Kd9l1k4IBOmwIM/znV2iOPU=OHJCAffCNPRkwsE3RqnVPoCRSqsPrio8prABauzu2pqF418Y1QJuJehhzztY8A6kwnBUkBVE7BIyLe7kgCnBoNZWElYki1xtaLksc/Afc0TjlZvv9IJ9fQHIBiL1JA9KcySq1tu9dv/NauykBODXuljPuVTk6I4xLLWcg20o26Ov57axp42uWPpcJHtasomLmmmnAXEh6P7aB/1Vlm/MAJhWXToxacauJzFao3F9JNEuucKY6y3RPDp1Qq3vL0gq98RKuiaejayu6RjyyU2+8vCBzURul8b7ZXPUHfIOME6Q5LvbKqLhe/mzqRc+9GUg22X3B5SYjdnXjwHbBTbihA==MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng10030000838D23AF@MicrosoftOnline.comspn:408153f4-5960-43dc-9d4f-6b717d772c8d75696069-df44-4310-9bcf-08b45e3007c9Matiasmatias@auth0.onmicrosoft.comWoloskihttps://sts.windows.net/75696069-df44-4310-9bcf-08b45e3007c9/urn:oasis:names:tc:SAML:2.0:ac:classes:Password'; + + var saml_passport = new SamlPassport({thumbprints: ['3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe1', '3464c5bdd2be7f2b6112e2f08e9c0024e33d9fe2'], // WRONG thumbprints + realm: 'spn:408153f4-5960-43dc-9d4f-6b717d772c8d', + checkRecipient: false, + checkExpiration: false}); // dont check expiration since we are harcoding the token + var profile = saml_passport.validateSamlAssertion(signedAssertion, function(error, profile) { + assert.equal('Invalid thumbprint (configured: 3464C5BDD2BE7F2B6112E2F08E9C0024E33D9FE1, 3464C5BDD2BE7F2B6112E2F08E9C0024E33D9FE2. calculated: 3464C5BDD2BE7F2B6112E2F08E9C0024E33D9FE0)', error.message); + done(); + }); + + }); + + it('should fail when the X509Certificate is invalid', function (done) { + const signedAssertion = fs.readFileSync(__dirname + '/samples/plain/samlresponse_saml11_invalid_cert.txt').toString(); + const options = { + checkDestination: false, + thumbprint: '119B9E027959CDB7C662CFD075D9E2EF384E445F' + }; + + const saml_passport = new SamlPassport(options); + saml_passport.validateSamlAssertion(signedAssertion, function(err, profile) { + const errorMessages = [ + 'The signing certificate is invalid (PEM_read_bio_PUBKEY failed)', + 'The signing certificate is invalid (error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib, error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error, error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header)', + 'The signing certificate is invalid (error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error, error:0D068066:asn1 encoding routines:asn1_check_tlen:bad object header)' + ]; + + assert.ok(err, 'The signing certificate was unexpectedly valid'); + assert.ok(/signing certificate is invalid/.test(err.message), 'Error message is not the default invalid message'); + assert.ok(errorMessages.includes(err.message), 'Error message for invalid certificate is incorrect'); + + done(); + }); + }); +}); diff --git a/test/saml20.tests.js b/test/saml20.tests.js new file mode 100644 index 0000000..63fc227 --- /dev/null +++ b/test/saml20.tests.js @@ -0,0 +1,294 @@ +const assert = require('assert'); +const fs = require('fs'); +const { expect } = require('chai'); +const saml20 = require('saml').Saml20; +const utils = require('../lib/passport-wsfed-saml2/utils'); +const SamlPassport = require('../lib/passport-wsfed-saml2/saml').SAML; + +describe('saml 2.0 assertion', function () { + // cert created with: + // openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem + + const options = { + cert: fs.readFileSync(__dirname + '/test-auth0.pem'), + key: fs.readFileSync(__dirname + '/test-auth0.key'), + issuer: 'urn:issuer', + lifetimeInSeconds: 600, + audiences: 'urn:myapp', + attributes: { + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'foo@bar.com', + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name': 'Foo Bar' + }, + nameIdentifier: 'foo', + nameIdentifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' + }; + + it('should parse attributes', function (done) { + + const signedAssertion = saml20.create(options); + + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + if (err) return done(err); + + assert.ok(profile); + assert.equal('foo', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']); + assert.equal('Foo Bar', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name']); + assert.equal('foo@bar.com', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']); + assert.equal('foo@bar.com', profile['email']); + assert.equal('urn:issuer', profile['issuer']); + done(); + }); + }); + + it('should ignore the NameQualifier validation when nameid format is "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"', function (done) { + const signedAssertion = 'urn:issuerp7iHnIt5xJZNimGNxh4d9R2J7DML8WNrMwMxmZ1WwSU=pb1Wp/LFbigEj+TNm7gkAwlfIc17LNwUXVTgM8RQnMvYJfIPZbl1yo5xMCh6ObMFwCs1T+gKI5C7jMloX2QhWD/XUffBKiDfkZUg7NI/Jyt5m+Bdst12SNhHBVsNilL9ZCuf+QtQD7301gUhVHP6Ramf4y+XNod9AfzhFLYNfl6fhf/5KA/KkjiOwYW5Ps/43OMXXSeVaeQ7JRU8XqyKbwlB+YXGseFLnyZopv8Cw9Bb2935ADLX111oFBkiRhnMUJW0LMbSWM6UVJ4V0qoW9h+f3isN5+R87RECNeAQP3WSBiddnEuSdhgQYQVnb6s0mThpvs7uvIOlog0FqeSrvQ==MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=foourn:myappfoo@bar.comFoo Barurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'; + + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false, checkExpiration: false}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + if (err) return done(err); + + assert.ok(profile); + assert.equal('foo', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']); + assert.equal('Foo Bar', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name']); + assert.equal('foo@bar.com', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']); + assert.equal('foo@bar.com', profile['email']); + assert.equal('urn:issuer', profile['issuer']); + done(); + }); + }); + + it('should ignore the SPNameQualifier validation when the nameid format is "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"', function (done) { + const signedAssertion = 'urn:issuerUYsy9NacnRqnSTbidM8WBBgS+Op0G05iBJTX0T1WlUk=VzRQaMR5Qk1P+g1tqJRKroq4JJx00FZ0rZxO4vG2gGkBXJ8262B4VUHOkxyPHNH1l/DuxSnNsL8AAbZfn8EdxMdToPvm2hkqygyA5W20o6g6eSC41rDOavTzesOKoXn3Uq9DOiUXve5ieYYCt5bQcoSCVT6uhVEKMhdcLhaB507qj9Gzcfp0E4F57ezRTTnVVEF/wCJ5j0QTMA2Wh09fxNkGijlE8KHzDJZapN4tDCzmK8qY7211gKuTfKYJGXYA4hSxw9fiQGDEPKRYA6tWf0HO5Vd8edRg2ZHr7AgjuCPp5Fj8VOP+KppA1YFBbq4Eqqt6KHg91KJlGs3ivpmwPw==MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=foourn:myappfoo@bar.comFoo Barurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'; + + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false, checkExpiration: false}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + if (err) return done(err); + + assert.ok(profile); + assert.equal('foo', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']); + assert.equal('Foo Bar', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name']); + assert.equal('foo@bar.com', profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']); + assert.equal('foo@bar.com', profile['email']); + assert.equal('urn:issuer', profile['issuer']); + done(); + }); + }); + + it('should validate the NameQualifier when nameid format is "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"', function (done) { + const signedAssertion = 'urn:issuerndqj65JwACeDERG2aZF6k0IF85KshkhgILzxhbKRyiw=LPcIU9W9HmX1QM+baMPLTj9StBFRksnDoFn/HVd8uLJgdH8Xeiv9TOQGElmSaBLypjCeN6ILq6pcZ0mxMC9zfd9X3WKmYtcrGI1BugATeNsqUm63x+Msau8pNuZrNNbfIQvLooMhF4T92ym2ADSm+zCQVNwBH7/v0rVIE6MEy8AYqqfpvH9CR88XQYMCSgKN0JQ2FPbcHvhIX7Hl+xG6PSzgfznE8dcWBUi24FajyGpqlNm8O3uHCfjR3wzO42UQIJFOJOiLb7QGNyWE1KXKYWyzZgAxGQuRUcbYxcnKTbVK3b3TBH+p2ZR+a2ktKmvqNBvQxy6tE4UXDIIpvmknSw==MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=foourn:myappfoo@bar.comFoo Barurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'; + + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false, checkExpiration: false}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + assert.equal(err.message, 'NameQualifier attribute in the NameID element does not match urn:issuer'); + done(); + }); + }); + + it('should validate the SPNameQualifier when the nameid format is "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"', function (done) { + const signedAssertion = 'urn:issuerGsh2DubHXYDeuHyBa+DU1k5G43UjyQsyPRYVgEqpTD8=mubsqLCaM16gT2rAFLl8XDuLWTALH6cdRMM/kNHLpVzO5PA6FGVPX5ojW2UCKGOhGHn0Hd/mYCOCtgAROphWjxpQl5TDyeQE0frjKs8ik0V/Jjy5T6PeWKHLqN6sHbP6YpkGixshCWtop8JOs6SijM9PBGnWal6Nx5bUMBfAyUnGyIhLwNaE8Z4NHkyAqmdxgLS0e8w5qngKQaUlyERZCrqKQ4w8VaHFG4Dos36XVfh+U7udo3IlbrpBLsu9xG1Azxe2iPC6+84xC09P6EvQylvTnz5NU4jhk10SmmRjZ6AVvzJTz/gbVfRfZoEAUhCuIIh3HuRwf400ESf38ouZTA==MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=foourn:myappfoo@bar.comFoo Barurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'; + + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false, checkExpiration: false}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + assert.equal(err.message, 'SPNameQualifier attribute in the NameID element does not match urn:myapp'); + done(); + }); + }); + + it('should parse attributes with multiple values', function (done) { + + options.attributes = { + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups': ['Admins', 'Contributors'] + }; + + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + if (err) return done(err); + + expect(profile).to.exist; + expect(profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups']).to.be.an('array'); + expect(profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups']).to.include('Admins'); + expect(profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups']).to.include('Contributors'); + done(); + }); + }); + + it('should validate expiration with default clockSkew', function (done) { + + options.lifetimeInSeconds = -240; + + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + expect(err).to.exist; + expect(err.message).to.equal('assertion has expired.'); + expect(profile).to.not.exist; + done(); + }); + }); + + it('should validate expiration with overriden clockSkew', function (done) { + + options.lifetimeInSeconds = -240; + + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false, clockSkew: 5}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + expect(err).to.not.exist; + expect(profile).to.exist; + done(); + }); + }); + + + it('should should allow expired cert if option not passed', function (done) { + + options.lifetimeInSeconds = 10000; + + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + // The embedded cert is expired, so we can use this as is. + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + expect(err).to.not.exist; + expect(profile).to.exist; + done(); + }); + }); + + it('should validate certificate expiration with embedded cert', function (done) { + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + // The embedded cert is expired, so we can use this as is. + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false, checkCertExpiration: true}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + expect(err).to.exist; + expect(err.message).to.equal('The signing certificate is not currently valid.'); + expect(profile).to.be.undefined; + done(); + }); + }); + + + it('should validate certificate expiration with non-embedded cert', function (done) { + const signedAssertion = saml20.create(options); + + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + // The test cert is expired, so we can use this as is. + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false, checkCertExpiration: true}); + const parsedAssertion = utils.parseSamlAssertion(signedAssertion); + assert.equal(parsedAssertion.getElementsByTagName("X509Certificate").length, 1); // Make sure we start with exactly one embedded cert + const embeddedCert = parsedAssertion.getElementsByTagName("X509Certificate")[0] + embeddedCert.parentNode.removeChild(embeddedCert); + assert.equal(parsedAssertion.getElementsByTagName("X509Certificate").length, 0); // Make sure we removed the cert(s) + + samlPassport.validateSamlAssertion(parsedAssertion, function(err, profile) { + expect(err).to.exist; + expect(err.message).to.equal('The signing certificate is not currently valid.'); + expect(profile).to.not.exist; + done(); + }); + }); + + it('should validate recipent', function (done) { + options.lifetimeInSeconds = 600; + options.recipient = 'foo'; + const signedAssertion = saml20.create(options); + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', recipientUrl: 'bar'}); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + expect(err).to.exist; + expect(err.message).to.equal('Recipient is invalid. Configured: bar'); + expect(profile).to.not.exist; + done(); + }); + }); + + it('should extract authentication context from assertion as a user prop', function (done) { + const options = { + cert: fs.readFileSync(__dirname + '/test-auth0.pem'), + key: fs.readFileSync(__dirname + '/test-auth0.key'), + issuer: 'urn:issuer', + lifetimeInSeconds: 600, + audiences: 'urn:myapp', + attributes: { + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'сообщить@bar.com', + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name': 'сообщить вКонтакте' + }, + nameIdentifier: 'вКонтакте', + nameIdentifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' + }; + + const signedAssertion = saml20.create(options); + + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({cert: publicKey, realm: 'urn:myapp', checkRecipient: false}); + samlPassport.validateSamlAssertion(signedAssertion, function(error, profile) { + if (error) return done(error); + + assert.ok(profile); + assert.equal('urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified', profile['http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod']); + + done(); + }); + }); + + it('should fail when the X509Certificate is invalid', function (done) { + const signedAssertion = fs.readFileSync(__dirname + '/samples/plain/samlresponse_saml20_invalid_cert.txt').toString(); + const options = { + checkDestination: false, + thumbprint: '119B9E027959CDB7C662CFD075D9E2EF384E445F' + }; + + const samlPassport = new SamlPassport(options); + samlPassport.validateSamlAssertion(signedAssertion, function(err, profile) { + expect(err).to.exist; + const errorMessages = [ + 'The signing certificate is invalid (PEM_read_bio_PUBKEY failed)', + 'The signing certificate is invalid (error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib, error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error, error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header)', + 'The signing certificate is invalid (error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error, error:0D068066:asn1 encoding routines:asn1_check_tlen:bad object header)' + ]; + + assert.ok(err, 'The signing certificate was unexpectedly valid'); + assert.ok(errorMessages.includes(err.message), 'Error message for invalid certificate is incorrect'); + done(); + }); + }); + + describe('validate saml assertion (signature checks)', function(){ + it('should fail when signature is not found', function (done) { + const assertion = 'urn:issuerfoourn:myappfoo@bar.comFoo Barurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'; + + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({ cert: publicKey, realm: 'urn:myapp', checkRecipient: false }); + samlPassport.validateSamlAssertion(assertion, function (err, profile) { + assert.ok(err); + assert.ok(!profile); + assert.equal(err.message, "Signature is missing (xpath: /*[local-name(.)='Assertion']/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#'])"); + done(); + }); + }); + + it('should fail when signature is found twice', function (done) { + const assertion = 'urn:issuerGsh2DubHXYDeuHyBa+DU1k5G43UjyQsyPRYVgEqpTD8=mubsqLCaM16gT2rAFLl8XDuLWTALH6cdRMM/kNHLpVzO5PA6FGVPX5ojW2UCKGOhGHn0Hd/mYCOCtgAROphWjxpQl5TDyeQE0frjKs8ik0V/Jjy5T6PeWKHLqN6sHbP6YpkGixshCWtop8JOs6SijM9PBGnWal6Nx5bUMBfAyUnGyIhLwNaE8Z4NHkyAqmdxgLS0e8w5qngKQaUlyERZCrqKQ4w8VaHFG4Dos36XVfh+U7udo3IlbrpBLsu9xG1Azxe2iPC6+84xC09P6EvQylvTnz5NU4jhk10SmmRjZ6AVvzJTz/gbVfRfZoEAUhCuIIh3HuRwf400ESf38ouZTA==MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=foourn:myappfoo@bar.comFoo Barurn:oasis:names:tc:SAML:2.0:ac:classes:unspecifiedGsh2DubHXYDeuHyBa+DU1k5G43UjyQsyPRYVgEqpTD8=mubsqLCaM16gT2rAFLl8XDuLWTALH6cdRMM/kNHLpVzO5PA6FGVPX5ojW2UCKGOhGHn0Hd/mYCOCtgAROphWjxpQl5TDyeQE0frjKs8ik0V/Jjy5T6PeWKHLqN6sHbP6YpkGixshCWtop8JOs6SijM9PBGnWal6Nx5bUMBfAyUnGyIhLwNaE8Z4NHkyAqmdxgLS0e8w5qngKQaUlyERZCrqKQ4w8VaHFG4Dos36XVfh+U7udo3IlbrpBLsu9xG1Azxe2iPC6+84xC09P6EvQylvTnz5NU4jhk10SmmRjZ6AVvzJTz/gbVfRfZoEAUhCuIIh3HuRwf400ESf38ouZTA==MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo='; + + const publicKey = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlPassport = new SamlPassport({ cert: publicKey, realm: 'urn:myapp', checkRecipient: false }); + samlPassport.validateSamlAssertion(assertion, function (err, profile) { + assert.ok(err); + assert.ok(!profile); + assert.equal(err.message, "Signature was found more than one time (xpath: /*[local-name(.)='Assertion']/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#'])"); + done(); + }); + }); + }); +}); diff --git a/test/samlp.functional.tests.js b/test/samlp.functional.tests.js new file mode 100644 index 0000000..e51a9df --- /dev/null +++ b/test/samlp.functional.tests.js @@ -0,0 +1,664 @@ +var expect = require('chai').expect; +var request = require('request'); +var qs = require('querystring'); +var cheerio = require('cheerio'); +var xmldom = require('@auth0/xmldom'); +var fs = require('fs'); +var path = require('path'); +var zlib = require('zlib'); +var crypto = require('crypto'); +var helpers = require('./helpers'); +var server = require('./fixture/samlp-server'); +var Samlp = require('../lib/passport-wsfed-saml2/samlp'); +var Saml = require('../lib/passport-wsfed-saml2/saml').SAML; + +describe('samlp (functional tests)', function () { + const samlRequest = fs.readFileSync(path.join(__dirname, './samples/encoded/samlrequest_signed_differentcert.txt')).toString() + + before(function (done) { + server.start(done); + }); + + after(function (done) { + server.close(done); + }); + + describe('samlp flow with assertion signed', function () { + var r, bod; + + before(function (done) { + // this samlp request comes from Salesforce + doSamlpFlow(`http://localhost:5051/samlp?SAMLRequest=${samlRequest}&RelayState=123`, + 'http://localhost:5051/callback', function(err, resp) { + if (err) return done(err); + if (resp.response.statusCode !== 200) return done(new Error(resp.body)); + r = resp.response; + bod = resp.body; + done(); + }); + }); + + it('should be valid signature', function(){ + expect(r.statusCode) + .to.equal(200); + }); + + it('should return a valid user', function(){ + var user = JSON.parse(bod); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']) + .to.equal(server.fakeUser.id); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']) + .to.equal(server.fakeUser.emails[0].value); + }); + }); + + describe('samlp flow with assertion signed with different cert', function () { + var r, bod; + + before(function (done) { + server.options = { signResponse: true }; + doSamlpFlow(`http://localhost:5051/samlp?SAMLRequest=${samlRequest}&RelayState=123`, + 'http://localhost:5051/callback/samlp-invalidcert', function(err, resp) { + if (err) return done(err); + r = resp.response; + bod = resp.body; + done(); + }); + }); + + it('should return 400 (invalid signature)', function(){ + expect(r.statusCode) + .to.equal(400); + }); + }); + + describe('samlp flow with response signed', function () { + var r, bod; + + before(function (done) { + server.options = { signResponse: true }; + doSamlpFlow(`http://localhost:5051/samlp?SAMLRequest=${samlRequest}&RelayState=123`, + 'http://localhost:5051/callback/samlp-signedresponse', function(err, resp) { + if (err) return done(err); + if (resp.response.statusCode !== 200) return done(new Error(resp.body)); + r = resp.response; + bod = resp.body; + done(); + }); + }); + + it('should be valid signature', function(){ + expect(r.statusCode) + .to.equal(200); + }); + + it('should return a valid user', function(){ + var user = JSON.parse(bod); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']) + .to.equal(server.fakeUser.id); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']) + .to.equal(server.fakeUser.emails[0].value); + }); + + }); + + describe('samlp flow with response signed with different cert', function () { + var r, bod; + + before(function (done) { + server.options = { signResponse: true }; + doSamlpFlow(`http://localhost:5051/samlp?SAMLRequest=${samlRequest}&RelayState=123`, + 'http://localhost:5051/callback/samlp-signedresponse-invalidcert', function(err, resp) { + if (err) return done(err); + r = resp.response; + bod = resp.body; + done(); + }); + }); + + it('should return 400 (invalid signature)', function(){ + expect(r.statusCode) + .to.equal(400); + }); + }); + + describe('missing SAMLResponse in POST', function () { + var user, r, bod, $; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback' + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should redirect to idp', function(){ + expect(r.statusCode) + .to.equal(302); + expect(r.headers.location.split('?')[0]) + .to.equal('http://localhost:5051/samlp'); + var querystring = qs.parse(r.headers.location.split('?')[1]); + expect(querystring).to.have.property('SAMLRequest'); + expect(querystring).to.have.property('RelayState'); + }); + }); + + describe('SAMLResponse with invalid XML', function() { + var user, r, bod, $; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback/samlp-with-invalid-xml', + form: { SAMLResponse: 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9mbWktdGVzdC5hdXRoMC5jb20vbG9naW4vY2FsbGJhY2siIElEPSJfNzY4NjU5OGUzNDk4YjcxOGM3MjcyNmZlMjVhZDU3Y2MiIEluUmVzcG9uc2VUbz0iXzM3ZjAyNjJkYWZlNmJhZWFmYThiIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDQtMTFUMTE6MzU6MjQuMDYwWiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSI+aHR0cHM6Ly9hYWktbG9nb24uZXRoei5jaC9pZHAvc2hpYmJvbGV0aDwvc2FtbDI6SXNzdWVyPjxzYW1sMnA6U3RhdHVzPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PHNhbWwycDpTdGF0dXM+PHNhbWwyOkVuY3J5cHRlZEFzc2VydGlvbiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgLz48eGVuYzpFbmNyeXB0ZWREYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIgSWQ9Il9jOGY1Y2QyZTAwY2UyMzkwYTJkMjdlMzRjZjQwZWI2YSIgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRWxlbWVudCI+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI2FlczEyOC1jYmMiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIvPjxkczpLZXlJbmZvIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48eGVuYzpFbmNyeXB0ZWRLZXkgSWQ9Il8wZjczNDk4NTFkMjY0NDk2NWE0N2M2ZjU2OTc1MDk1MSIgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjcnNhLW9hZXAtbWdmMXAiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIiB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIvPjwveGVuYzpFbmNyeXB0aW9uTWV0aG9kPjxkczpLZXlJbmZvPjxkczpLZXlJbmZvIC8+PGRzOktleUluZm8gLz48ZHM6S2V5SW5mbyAvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSURPekNDQWlPZ0F3SUJBZ0lKQVBQb0hyRXBiN291TUEwR0NTcUdTSWIzRFFFQkJRVUFNQjB4R3pBWkJnTlZCQU1URW1adGFTMTANClpYTjBMbUYxZEdnd0xtTnZiVEFlRncweE16QTFNRFl5TXpBek1UZGFGdzB5TnpBeE1UTXlNekF6TVRkYU1CMHhHekFaQmdOVkJBTVQNCkVtWnRhUzEwWlhOMExtRjFkR2d3TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUtiOUdpZmYNCitLdlFQd285ZW9PYlNXN05OWVpGclVveFJuNzRxTWNkZlp3a3V3RzNPOEVHaThYK1VzTnROZ3dRbE1WZll0OWxLQjcxaUpTbEtPQmkNCkJQU0ZQN3pQOWpGdFRuZkpjYVJ2ZHZZUG9JQzRZODF0dTZMa05OM2UxLzMxTnArUjZwZDdGNkxmSFdxdWYrQitoeUhKQ1hhc2RkNkoNCmxHb2ViOTQrZW1wajlsbTh3SE5iM3NyLzg4Mzk0S0ozRlVCZXhQelE1cnBLTGU3ZDVmbTRFS08vaXlFcFdIVWxmN2RmOXlHRDZtNzENClB4bys4cjhEcXE3QTVFaEdYL3prNlN1d1o0ai9zeml6eW4vY1h1bGxHZzNQQXNjOVhYTFQ0NTVBMUtFQng1ZVRHck1jN0pRM3VEVXENCnFmRGY0dmp3bE5CY0lqeGcyWDNkTTBzSlZrLzVyMDBDQXdFQUFhTitNSHd3SFFZRFZSME9CQllFRkJzNWxwZnZleU9Tb3BtTlZlZWgNClhQK1BHdGszTUUwR0ExVWRJd1JHTUVTQUZCczVscGZ2ZXlPU29wbU5WZWVoWFArUEd0azNvU0drSHpBZE1Sc3dHUVlEVlFRREV4Sm0NCmJXa3RkR1Z6ZEM1aGRYUm9NQzVqYjIyQ0NRRHo2QjZ4S1crNkxqQU1CZ05WSFJNRUJUQURBUUgvTUEwR0NTcUdTSWIzRFFFQkJRVUENCkE0SUJBUUEyWmszU21TR1RPaC82cmF6ZW0vRmk4R3pFb3BjS2RJRTF1ZUNoVHNBemg2L21pbTVxNWxIMFBXMWI4NXNRMy9jMzFTWVUNClN4VlpCODRLMk1QNitod0MwV1p4a3E4eTBpTUVFQXhXeUMzWjNpOXBTbEdkdzdzdi9OV0p2NFlQam8yc1NOSHVaODBPMTFhM2NYb3UNCll4TE84REJSTXE5VlRzN1JiN3FLRkJXbDVJeCtjWnhWZ2xyeEl2NlcwOE9ycm1xUGVvRGp1aUppQmoyOGNzamhlaFlFbEtZY25VNEwNClJkSWpCbFpGbjFBb1RKUkJGQXlqTDhCdlNNSU1Sa3pFcm8vR3AzSXpqNjAzUkJUR09rdm5pYWxLSGN3TG5WRkZFMHhlWlpVcTdLdzANCkx2TzBYOHVTM0RYN2RUYzJvcXpYT1R4NDIvT2o1cTl4VXVhaXVYME1SWlQwPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PHhlbmM6Q2lwaGVyRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiPjx4ZW5jOkNpcGhlclZhbHVlPlBtN2dVRDI5d1AwMTdLUnZKTmdmVzUxRkQ0eHlUTGJ5RDdXbElNRVZUR2xzWnc5K3ZNbUdzL2VkdXJoT2ZVZEV2SGZBV04vdUYzYkxCOTl1Q1pFN0dHLzJ0aDVBS2pLejFaN1NvZWZuUU54dnFvbXUyNUNmWTEwUzFpbitNMU13N3ZrcTZlS0c4bndEQjBDc3JsOXJ6ZUMyekNQRFc1TG81N0x2NDNNbUVpM1dYZkVhbkQwZDJZT2NRVFppaHIzUlpnajl0SDJUQmVKZjhNN28yY1BrOXFBWk40aU56dk1oWE5OV0RDR256SGxIdXNxVk9RNWM4d2l5MmwzdWlUZlk3aEIvTVhpUDVmemRPYitEbWw4NlJrT2MyUVd3RHUwQ0t1ZHBveVRxQW90OUhFZ1JoL25tVXVSQkVKQ3NtWEdyN3FOM3ZSWW5HTXRmdEswZG9VYzN6QT09PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjwveGVuYzpFbmNyeXB0ZWRLZXk+PC9kczpLZXlJbmZvPjx4ZW5jOkNpcGhlckRhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpDaXBoZXJWYWx1ZT4xY1JHdFhpWFZMYXRhSHFTMWVLM3JtTjlQcnJyZ0laR25XM0xPT25aektuU2w2dWFKWnRENXFyQkRURE5zaXg2ek15cHJPOU5KQVlQYlJEQ21CbWNaVStMSm5aSVdtT3IrQ29SdWJBUGdIT2Q3Q0VoQVB6RGVJZFh0S0V3R29hNHNKbXJRMjlCR3VSejBYS3ljTlJhU2lJa0toRm40dTZCRzV0a2dzS0RRT3Q3dmd0RzhuV21pczZqM0lYSVdrWGpXT2tKWjBHbktXajJqd3EwL3ArdEo1OXM1R3lTQi9uVy9nQW1PcjlLelhJMllQWm5CTk1xSUl6ZTZPNDBaaDRtaXpmUmdTSzM4dE5XQ3NCRlB6Q1krZjJWZVlsMzVORythdUhEZU9BRGtkQkR4d2xqRTJCV0Q2MXBKaFhuRzBTRVBIcnV1b1k3YldjWGpoNDVYLzhiM1hNcHF4ZDFjRFJPZkZMdFRMb0syVjVqcnhiRGRvcVh2TFR2NnZ5NDZGVkZYRVNEV0xmSGFxTjBOTlNsY2hORXJmcFV3SDRNUXNVOGU1UW1yeEw1QUVDVldmQytEYnNOZTVjZFhZcS8wVTBkcWJPV1ZlQkdVL3h0KzJEWmVhSFBTZVpweGhCb3diZ0FKejRlTlpQdnhIWVRGZHducXZhemRneWo4VUdHbEQvZjFoSXFHWHFja3BNbTJ2OEplZUd1akV6SDNnMXNOcEluWG40eVBaTUFLSEtOaVhIZTF3aWk1V2V5Ty9tMHlCUGZreGRrMll1S3NPMy9KWDlOcXltNXV6WUIzbmZZN1JkR21pdXBDV2tpb05aL1hidG5Bb1dDSENyc210am1kdm5rM2NBTVAvdmNSWmluQm9pSjgxdnBLdTNtSm5FdGV6cEVqaUMxY1dnTUxoLzZRK29MbXZOL1dyNVZWZmxjSFJYMWNhTEQrWURncnkybTVCbHBTRjhVKy9KYXR6NjR3Y0V2aXlqVFgwelN6VlRtakptZG44a3FvSEN0V3RvVCtWQUt0MG9CcGRMdHg5eDUzUTVTVG1rUG5xOXdnc3FWRXBGZGFHK25qM1RiZkY5K1FBMHFCVk1BVmxpZmNyb01YTzZRZlpFZUwwNWpFZWFSUGZpK0t5K2tVNUUvMFduTWFVUU1ZZE1kUFh5NW1XenZuZHMzVE5DczRjMWhYc1c2SXFoeTRnQjFPdEFVdjhIeEtPWVhrT0ZMMVN4TEs4Ty80UUUwVEFGd0hvZ0hEY3NVTEIvNDJHZ3IxZjZJVWcyTGNjOTMvN3JOUkhOV21OK21ienBJdUUvT0lRTEM5RW5yM1ErSkdURFJKajJsR0Fidzk4MnlsUzYyS2ZETDlKMWEzdzdtb0ZKM3ZubTdTUmZ4K25CREw2YzFBSDJTS0s1SmsrT3VDOWVSWllwMDdXMy9TQnJOWFVUUzhpbnpRaERLSWwvOVJ0b1Bzbyt3K3RnaCtIY2svNERaQnV0cmtpY3BGejBWZm5pK0VGZXBEZVJLT1ZwY1JNclVFeHdvWFZ3SitBUldMRnBIdDFJQjh4TmJTeFU4WUpzeDlMdWxGeXhUQzVOYWFqU0N4WFhkRENFajE5U2o2MFRYZGcwOHNRY0NYdGxUS1dLVDdQQXl1cGo5dFZtdVg2NkEweGQwdWt3MmhjbSs1bnB4REtEbUhKdWFMSUlzSDB4WDFyQVE0a3VqVHg1aGNGZnEyMDZIK2JBUE1xTEYyTjFDSnlkYUJMd2xoejRwa0tPczhMRXVzTGtySGVUK3hFeHBETnk4MzBZQ3M1SUpnTkY2R3dFcFdiaklGbVNIMlpzSnBycnB4b3h6eGtuZ29DSzYvSlRkYUVoM0diWkVmaFNlSW5kTGFnUktFcjZ5azNFMS9sNzlxTmxCbzlpUndTRE5UVXdkMmVHOENhZVpyYjJQZEIweDk2L3Z4L2lkUXNwQjNCQjkrbzBRaFhndVp5c0pDMmtIdmZUSndZZjRWVDBoL01TQnk1dGVWYU1kUWZxUmpCVm9pbGZncnFaVjlPTHZOOUw3TzUxVGFqVE1EcmdleGxIV3VNNVhMczZUK3pNdXM3WisrNys2aGNEUGg4UXpxY2ljVyt6TGpOTHN3OXhZS09CNlJ4T1NHTy9CekkxcExhbmRHSnJ5bXVsWlBicUJyZEQ1MXFWNUxwWjVBcUU2MHg5RGc0NDNJRkpPeWIxUzN3VCsyb3BBVEFDVDZQV3o0blJsNHZJc2J1M1RlVGdGTXZiUHdKdmV0U1d2ZjNwckdRNDlqVHpMaXhlSUlRTmUyOXdYamZROE1TKzJSUGppMnlpQVFCcllITWo2UzBiWFloYUVCRmFGN1kxNTRxMVJORk5rTmpmN2tVQjBTbVlnaVU4em83QjdLNzRwTm1SbWxIcitMcGtFcHVyVEU5Lzgvd1hhRWFzSitIb0UyNVlTYnFZRitxKzQ1L1NFcEJQdWpCa2ZQdkQwR0pqVCtnbGxiNzFWQU9nMTk2dGN2RFU4TUduSUN6SGM0ajJRaWVhZkF2YzZMNElvMEJRQXhnNDZXTTlIWVJyOGhZeURQNzFCMC95Sm5CV1R5QzUvWFNsRmsrQWFQeXpxdFZpMk5GeFh3bnRldzA1R1NjU2F2NDdKSEFRVFZaRlJsd2k4RkNtaUpKZWVQVWRrb2FlZExOT21oUXFMN0hmZ000RDRmOE9MaHhxdng3Ym5QQkRPMmlBcndYbHFrdy9Lc3l4dlQvZVd3NUZMOW41KzVDTWxiYnJCTEpNM2o2WkVxM3RJOWZuYk1vNmFHUERRb2RURUMwaEY2ZHdJYm02VG5tdFlLcDFZSTR5UWlzMUFURmIxbTY4R0htS0tidXNiMGE4N3BKbzRYbDlQdVdLSFRhMXBWdDBVTWw0RWJjNm14aXdyRytIZzNFdDRXUk5qaTJHQXhWMmYyekFaN1ZsNXpGRk02dmdxbFlqQ3NWZWNLekM1emp6T2Y4aDd0UThKdTUxMWJpRjlhN090cFhCWHZqVWdvcVRFV25SM1pyRjI4MkNNV3VvUXJHbjlwNFR2eldORU1iWjd2cGJiQ2tYS0t3cFhhWXNzL3pwcHg2d3JXWTRNOUdoVE45RVhXTTRXUGVpbmZ1MStUQUdDK3dISFVTd2h2TE9Ob0RsaHhqcU94MVVweVZubmxTeTV2QlBIbWRrYXgzT3o4MWhIY3hHVEtvb3N1U0hRYU9hL2xRelY4Sm9sM2RyZURobjlBWkM1bjRsa3hYV0tUOEhuQUNMS3VUTE4rMXpQUVY0UWorSDcxNUloNkZzNkE3bGY0dVloY2pWcWNVdWZrTHFvcjd0NjdOck5FekIyNSs2UVNRSlhMbTNUU2M2UTlCUDNjNUhWOGZzVHJEL043RUdJUkJGL3BTNldSMzY2UjV6WkJiWjZXbFNOek1TcnlwZnlQVDk5ODhJeGtLRFZqYjE1MkZ3Njg4OTZvUS8rckdxY3BxYlB5bmsvYkpVbThWWU1vbzA3U0J4Y2hURncybmtRc1VLT0J2MkdJT01WQ0d0YUVmWDh5YVBlcGZ4UGFxSm5tVjJTQ2U3Q09vVWQ1b3g2SzgrL21uWUZaTXJ6N3RYQ1VIU1MybDdyU3BYTStXZ013VnIwalh5OXdYSkxKK3NLM0dXSDZYVDAzNHZrMk1KZHdmcU9zQnBwTkFjeStNekd5NDlFVlFULytQb2FlT245MzM3aHBibjFnVmNMd2ovWGMzRll4REtkakY1OGNUL2d0dG52aWRhaktUcGNnQ1l4R1F2KzNRUkhidWFEWnRyRXY0L3phNnpiRGZHcEtzbklqeVVhc21HeHpiQ1Q3ZjFMQU1aYlJwaklnYi9ScVRnVnhEQXRhUGNSVU9YTEE3N29ZZm83Y2h2STd5RlJGbk9lMDNURDhHRFFLaGI5VUlzN1JmRXNYS0xVOVZwZVp3NnZ4Q1lNZDhScTlDbG5kOG1jR0FsNDgrOTRVeUFWZUdUelYzMWVCam44QjQ1R0wzWDkxL2JULzExaHZNS05YMjFQbUZ5eVR5YzZNMXhVY3IzN282YWY0SEFzVW5YZ1o4U1VLUXdKUE1WQzkrRW52RHByUjYvWS81S1l5MFVnb1IrR01IRTNaMXl0UUdZWEZZRlVaVW1NVjJVZ3oySW5pVTRhWExYNFlxQjhMRVgvRjVENnUzcVFCekhkSXhPelNTdlRiRXN6dVhuOFd4NStMNW1MVGM1U1JuNmxQZTRIUVZ1U0lLYzg3Ym5UTDFzNHFadjF0KzlaTUxLSVRHc1J4bjM1YXFZdVBMdDVOM053MlBZUkg4ZHFyZkwvWlA5WW5CWXo2aEFlWXZQVG5pYWF2ZVYzU0dNeW1VRVM5YlVtbHRjOUo0NnR3MkNwYkkxT0NDRDJ3dW1ucTlvbkZEam1oQUticENkaVNlSXFyNll4Y09QbzFXTnNVMmR1Z1Z3L1dYbTl5RUJ4S0o1UHJjNHJhdjhPT2Ezc0g0Z0cyYm9KeEMrMTluenZDbnVmTTRiRVQ3WVY5SVdmaDJLOGJacytRYTZPYjBTOXZnUWw1YWhGekpCUGhNUW91NjhKLzNkQm5ScUlPUENUMUhxcDN2aktlR3B4bGQxRDl3azZNYUREakl4aTJvWDQ3azZYREt6UkprUENabnJFTDZSblRodFJ0dzhiRUJCblZvdTdHWlZMZWtmRmw1MGlHTldBTmJpYTFGaDhad0c3SmxGbnhQZkhOdHYwM1QrT0dmOGhnMDV4YVlWQjRtZURKeC8rWmJBQzBKYUpYeUpNMnJmeTVuY3UxTk9BNWdEV1E3Vkp0MlFyQXh2cmQ5TVV4eHRnbHRtek1Nc1FZbDd3QVByMk5QRW1Nck1EeXJCcVRNY0JWbVg2N05sNGhBck44WUQySE9WZW5pZkkzcGFZSXMySTg3eWJ4SVNsMlEvbDFySXdScHFZSFVzRGNqVjBmUzd5c2VVZUdnK2hEMDdZR1RWaVkvT0JHeEswMzJneUtUcFdVMHFibW8rWTYvWkdVdnBqbldCRWJ5QkhkZ0xia0JUL29CNVE0azdLMi9TQXd6N29DZStJWG9OYkxYYkFNSGZIS296WFNxUysxQkN1TUUxSnNsWGpSWFk1WWJnRms2MU8zUndLRFVMaExUNUdmSlVFUVNoU2h2S05naHYzOUlBY1o2YWlFWlp3WVBUK1BudnNpc1hKanFiM2lhaU1yMCs5aEIvR3hrOHN5amRuVU1mL3FJOEpRU0k5UVhIZ0t6UU5pY0dSZFdjNUpXUkZreUIxMzh5dUF6NHQ4eHI1SzFhNStBUHJ6M1JJem1KWExpSlBySkhhcnlTL1ZNclBKaHR4WFpoYVlsYlJPRFptaUJjci9LVGlaVk02OWNlK3ZTa2FtZHFCakxFTThNWHZXQzRUY1poSDUzZXlmblJNUjIyb3hKVEVlQjZDYWJFZ3diNjNtQXlVb2JUSkdabFlvZ2NpbTZXZ3MwWnFDNllhaVR2dzNuLzEwWnhHWE5XRnNMRGhDa08vTGxqQWh2SXo0VElnN2F1UCsybXRNc2pram9IVDRDMG5QcXhGREZHanVRRHJpblRDZU1ZRGtnVTBhRTZtMzNnRGp2UEZDbGNqTEc4bTd4REtKbmwzTDNsVCs1eUNJSTNvY2NqaGxpTGVlN3JzSklkeFFaeEd3aDB0RUtmSThtdGYxbzlOSnllMEc1UnpXOTdiOXk5T0hEaVloTWNtalFHVHA5ZE54a0h2cU9qak9QbkdBL1FYZDcwYWVHVy9PT25MUjdBdkRzS1owbENrZDN6dFh6eVVLZ0VwQWRHK2FSbkNsT1JodG9xZXJwN1VxUHJvTmRGdHVNWStnOTlyWU9XS0RxSEo0ZFBBK3lRTkFRTHVBNTBLMU5RMHgzbEdlSlpaaTQraCthN1RRdDF4UVNqODBWVk4zR0VJc1I3WE02RXRNa0JLa2h0NjZoS1pQejJpT29mSVl1YUl0QkJiUWRZN3RXTW9LMElUL2ttbVMrK25MSmJiOUh2WDdqMGxvVWhqVFdocHF1MDQzcHRVeVBvWlVEaEVicmVsVzJldW9kc2ZuNHhoN1A1VUlaR2FNVU5HZ2k1bnF6a2p6MXVzaDEzU3huN0g1NTdZeGlZV2p2TmVJWmd6ZGJWK0dKdERDczBoVm0xZzhpbDh2MnEyazViLzlRZWh1NVVpTFNVZ3AvYVp6d1hOL0xkajQyVWxpWHM4TWRPOHVRVFRnZzZDb0dEbVcvaGJZZU1BV3NFelBlUGliRENmU0t0c0gzTlNGWWRUU0hFdjJYUU0zOEkvL2JpTlR1VWhSNzYxT0U5Qy9ndVFmd1lnbkRJZ05uV0NPUTdYRXRqSHZ2SDM2OUZiSEtnNlpwVmlzRWlEMk1qYnpCbEJsTm5FTFFBU3dQOVVBME1iWVQ2L0xuS3hYTnJXTDRwYWs5WjA0R3FBTm0ydTVxcFp5WGZFZzVGWXdUODVHbUpmSW9JQ2lCSk1JeHE0bXlwdTZ4dWRPdnhPeVVCZFE2enQ0dDR4MHp0TWQ3KzlNN3YzdjNjTVZOVFh0WG9NUEZLL1NKQWRaSGVxb2ZIS0FtcEJiaWV2ODhWL05XaVR5SVVUT1c3bkxGdWpSYUtPODZmWnB5c01kKyt5Q0crcEJDZyswZFY5d3RuYjhkdnl5d2hENkoxTExoelQydGZhUU5mWHZ3VUw1d255M3R3Zkd6M3NhV1A2YkYrSmpZd000SlltYjdEbUNySTZML3M5WlJ1TWlGclRFa3JqNUJ0T2oyckpObFlwZ1NWZ2Z2WWtLWDhmZFVhdWczRWRzVktheThkNzRwUE11S3gza0g3N0ZWbktUdWNiTEdSS1AyU1FuT1RvbE1ZTHNlQUhOK2phUkNpODJldTRFVzBxbWNmYjJNM2JjNVBzd0s3UUVQSWVnYW02Sm5jMFhOeUVPU2NWU1hmUUZEU3FGME1jSkVjeVUyQmIwaFgvTStNazd3SEFRUjBxa2tOQU5KYXkrbFAvQVliWVJ1dUtPWTdXRUxheHUzM0dhZ3pZeU5scWJFR1BQQnBTU2tTbCtiMXEyZHRXWGhmd3JXV052Z3lnMUQ4eVN5U2pJUXNaTEUyS1pab2RuZ2labUtqUERjd3pDMFBvcENSVXQ2ZFBZM3ExWTJjMk9Bd1BpY2NkN0YrYWlSSjdzNWpPei9BOHp5NlpLdll1ZW8xSDdoaVd2dFlJZ2YrQy9MaFFuQ0UxR21rODNzdURPQ3JQSUxPMDduOEcrMjZoenEvQ3lGKytGdHNyb29XUTErb29TanNmQWpXaDZ1QUR2N29DbmN3OFdmdjJXVk5pcGVHZU13UlJpcTFuVWFGN3BwK3dONGdsU3NVZzZ2V21GdHQ0QURFeXp3ODBMRExPNGszeXpsQURmc3pJeFc0KzNsT3FKN2loaklvSkMzRlB6djRWNXE5VWFlWjB2NWt2bk5YTmZUb3k0eG95dFROcUVTODFZYjZiK3R5R0pqZFRyai9WZ3l1dEU1b2oyN3R4YU9ObGloYzhObDlQbzZZb3ZrK3BFUU1vMEQ4SlVXNi9qL0FKUnVTT0I1MllMN1BJK3FFQXpIY0tYTXg4d3l3bEVHWEdrako5QXBoWTA4MUFyazJWclA3UEJEd2xwcENlQUtjZ1R3dTNtN0lRL1FibzFQMVhwMlB6U1hVbnI2N0xiRmFTT3ZjalNtNGlBQ3FjeXVPaDFVb0pIRGRRZWJ6UTdrWEdiQ1R6bEVPZ1NRazl5RzJMY25mOGVaVHBvRXZTbUpRNGcyTEovRkpjelBheGI5cUZXaTF0cGt2OVFrd04vTTVSVWd6QmNjNjV2ZUZQZE1ES09VRmU0UnNtV3htbFo2ZXBwQmJxVk5CbFhoQVBIWSs1aXRjWVR3eHVLUGJNRUNjcDYwc01xUEEyMDdoOWl1WVdvclRMMmNyMjQxSlBDQ3lvZVR5TDZ6MjhmbisyQXR5akxCVC8ydXpaUm9iejY4dzNDRUpLU1paQVlURG16ZWpaWXdVbmkxQ3BhM2JEcDNZcHd6ZkFIeEtSbmtwblJUS2gvUmFYVy95RElZb0U5eHQrcFBjb0kycXRLWGZpMjcxRXQ5eUMwTElvRllFQ2dQRE1GRGp2dGZlQTlnS2lpbWNiUThSSGdld3c2c1VLRS81L0VJcGlRS2t0UC9oSUtna2VHbmkxSXJHbnJYNU5aRmVlN1R0V3BlK3ozZUhCRFdJNlh4TGx6a3Y4ZzdQS1ltSkpxZzY2Unl5K1UzSlBkbk9pSkxoTWRzSmd2NDRlNm55am5VK1V5RWRpN3VGTUdoaFdOUlFSNWg0Y1JRMTB4MXMyNUdyUzhZWDlOdWVZNk5nMlAzYUJ4R2E0enZMa3YrSzA2czN3dUl2d2piSWxkdmpqdTM5di80eTlWMlo3VGdRWlFVUVMzQUd5NmFmQ296VXNnN3pFQitOZmx5U0ZwUDQ4eGY2U0NHWFcrbXZ6NVVabWlEMUErNkFlRlBmTktFd1lWZFB2OXN2aElaWlJ6cVQ4RTRNNCtaVUlxS202ZGplRHp1QmdGaEV4djNZNzZlNDd6OFhCa1p0SFV6aHptNlZPTW1rT2JualFlekdWTTBVdnlGbzVYWVZQbzhTQlo5TVBqd1NVbm9CUzB2T01Zb3BSdTBDM1BQR2NVSVFuZ1BncGF2WVRlTHpTWXZzU055WEwySnEvRUd4eWJUekYwN01Kek44VDN6SURUcE9FUjRUOGVGMEl6czVLcERSdFV6c1JLQjFwNm5RcnFmRFdTcE9VRDB1cXU1U3pPTE8vZkJrb1V5QWlWaldtMUFiNWxtSjZWMW4zRHJLazQzVVBMeTJ2MmFMWWZsMWR0QXFkSHBqY2dwaVdLQVptVCtBMU8zMFRLQlVQR3lnTlN5bW45ZGI2ZkJFQWphTG9USG9DS0QrR1RzNTgvTThpb3htM1NUd0F3K1FJUjAyV3RYTjJGM0JpQWV2Z1pGR0xRNEk3eVVvZ1RXdzh2VnFiY0pZWEZOclF2NDl0eVRBcjhRWnhpamIyM1p2Y01EM2ptcDBNbURoTGZza212T0d4WkdLeW9ERzk3b0k4cW1mbzFjQjVxMVUzSk1NSDFudEFiZTl6MWZFTVNOeVpCenVMV3QyQk9MTWU3akNQR0pYSThmKzUrcllmVGJEVlRxeldQK3lrN1pFSVF5TW9kcitoSlE2eEQ4Tm83Q255KzFpMlR0cU1wTGF5YlowUDVDRzlNQ0h6eHMwbHp6bjJiRVFyUmUxVlg5TUJ3NHVmNFFCeUZZZ1phUmN2NzdNdis1alV5QnQ0amhVd3dxdWp3eTRjL3VYdytQQU9iMHBsZ2s0SG8yUDcvUm10bmE0bnZBQWo2Rzl2cVBVS3pqTnh3RmRkRWxFb1ZERDUrK3Q3TnhhYVVXZzljRjhLRFJZU0M0enUxbXNXQXVYZFQ4aEd2Q1lmNjd4bTc1SVB1SnRtZ3FCWmpYUjg3b0NKb3h3aXZ5bzh0dHRxbUFaOFp4K0VCY1hqV2pLMzVKMzZvVFZ3U2JOcnFJYjZLbmpHbEZYSVN3clBORWkrUmtMb3RhU3lPU3ZuS251bERjM0doaEVYYXNCY3dEMElPNzNpZEk0YmlnR2F1bW9sSFF4QWVMRytVOUZycUY4dGdRT1dMenY0aHd3SlFHL2NpNE5GYmZISWpEVnM0bXM5WTdzQldUNkI1T1pNTzZyRE1wOUlsZG1Fcm1VUGE4ejU1ZVdqMDQ1YUxEQkt0Q1k5UHpXaHZXWHhqeTQ3SmxCU2ZETVBNVFhReHJ0T1B1SjRLSmo1V05xTlg3WXoxaTJMTm5weG5KbnltOWNsVTdGV1MrT2dUeTNBOWpYaTFCVXg0QjBHZzhXWUl4LzVBd0pzU0RPVC8yV0NpM09jRXhtUVRveVZrQ2p5UE55ZWNOck9GQmRQenowa1NaQjNJaFU4dW5OT1N0YytaNjdOZnF5K0kvRTlaVDczcUNCYVRJN1UxSFZxQzlsSnluUk1zbUZmMUdvbGNROG5DbXZvK0tPVTVtVkVzL2xvQThIZ3pWZWVNR043SkxaOFhJcUVkbnp5NHQyNlNRbTlobVY2ZDRiS2ZjQmk0MG9XNGZRUFdZRG1pWlorR1MrTDhuSnlYdzNBWkRIWkdKWEhFci85S096RFF0OVU0WVdpYS9NMTIrbFNyWmd5QmlrdWxnbnBBYmU0ZERpMUZMQTZMMUQzQjBIUlozRUdHamRRbng1R1hFZXhnNG9CVlQzS2dRbmpweXJrN0YzR0tQOUhGdnFLTUZ6VWJoL2RCV3k5bjRWc2JXUFlkSDdsSDFyVUg0YzdwN0lrWGc0VlREOE11MzV3b3ZORkErWkVBYmR3Z3o2dTR6NXZpZzhIcHNiUVpNVXZFVy91VE83cVU4UW56Z1o1NHRHcGl2YyszbnBhYnhSUTZSMDNpMHZYdDZCb0JJckFBMndxMmxzMTVSTXhleFZWczdYT25ORDRlcHFoMUJzR2pROC91ejZya1dRNVJFenNCeERxbXpZSTA1UXhYZlducUM2WVloWXpON1FQUzh0SGpYbGxsN0VzKzhVYzhzTytjRkE3REM3Y0hRbVZGaVpXN1NZYmFtTmFHc2FpY2VYTHE4Q3YveDJIZ21oUzYveU1qUFVFVkhoOTVZdHdhc25kT0p5NGdBbUlEWkh0d2RsOWxXY1ZDMDl4MUlBakt5UWlpQjRPLy9DcG9qZjFOUmNiUWF3VVBKMGI5ZXJyWDNFVWM3cU5Vc2NGMlpubXZVVThxM1g2UFVpb2p5TDVGMUFNdWJzcThuOTFmaG5FUmwrY2syQUJWYm9yeEhORlpSVXgvVlNYNHRMZzROUG1VcmhlalNoNXJZUkl6S3Q5OG9PNTBsTlEraS9RVkZUbmt6Ym9SUkkrS0NTM1o1bzJZZ0hlOGx2ZGhDeHYxZ3hjVGhRM21pd2dIeDdmR09LV1Myc2psSkxYanMxbm9raVowTEZ0NTIxaWYzKzFSYVZlRHpCU1hYRFFZVCtwbEI1S0M2L0ZOZEpDWW9mZGVTY0gwK0lHN2FyeWRyOUhUOU5SMXNHczZnc2JYQnNnWnViNXFKVmYzSisvQ3kra2FCYzByTEtyZ3UzZmlGUG1uMDdhWGdtK2xRVjBPVlVkaWVoNm5vQ0oxcHczT2VNdWJ0TU9rcmQyUWRFNEQ3T1ZoVithdUM5b1pIc09uQXhDYnZoYXovTTZYOEFaNXducHUvZVdwdDZ2WHBhWHpXVklJcVBET0NFMnRCSTcrNlNmTHdjVXFlYU5ZSVhtUlRwNnRUOGhhcDduZXpDZldqUGZxU2ZMVGljaFpEU1dlR2hYTXZWNTRCODJ2eWJoM1NBNXZCZDdwRnkzWTlxYnNCaHZDbHRKWmwzK044OXNnbTNJWElyeU5MT3JmL3VUMVJ0NDRrVDNleFM5N3FCWVpteGZXZW5PeUxqOHAxZGJlSjd6MlFxeE1xZXRia3FOUHBucnduMUFMV3VkSU5ZOGhLYnJYS0RDQmhWMmNQTkg3VWdTcnZYVFV2T1UxRnVyQ0FGRThSa09vcWxobUZ4SXRRUHUzNWd4MWFqaVFBTkMxQllTdlkvZzZyL2lLMHRlekFoRGNtaW4xVWF3Qk1qMkZHK2M4ZXdCYzVVbzN6ZUNIdERYVnlsSlY3UUdGVGZZR0JOWmlKUXJkYnFXRmEra01tdjRJUWYzSjY5bjBEeVNwRTJVY0h5dlFKQ3d0czlUN3NMU2h6WWFNVFVKUUFmOHhwcjBXKzVac3NHMjhJcXFsSTNkbUFpM0dQa2REblE3c0ZiNXBWSVVFVHRUQm9kMEp3K0U1UGgvY0Evbk9FbHpRYXRXUmUxWCs3NFl0bzdZSWJBRW56NW1YSGlva1hnTkVzbEdUWXhzWUo5QVBWOSt4WjVKTk1VWFl0OGhrVHBwaFBzN0FlazFpOXhsMW5sbm5FYnY0M3hnM2VMYTRySDBBcy8wTE9GK01yV2Z0aDBoWnQxanJCb05ycmNHOG1WWVJyd29YaXFIS3NSZTRVYTQ4a0YvRXVRWFZKeUZPV05KNUJjYW9xOSswa3lJdnVoeVhxNS9UL0tJTG12cFpTMzZINHJ1bFJhRm1Hb29sejBtaUl4V2FrdHFpUjF1VjhYaWtiekZFUzR2cGV5NHVKMWVBZjUyRCszL2NLY0V6V2E2KzU2VGRFQ0VKQWExWDl2Ni83UzFDZlBxaVkwN1lNMXZXdXNHcEhPQ0FSTFBDWGZ5V2ZXQytVSThPM1FkSEhRaC9XNXpzZ3RFWnVhYWhkWk9lY2VsQmtnS0NIZ1o4NTFpN2IvY25EN2VsRHlCVGJuNHFKRWhSWVRLWUNRZW82SEpFd1N5b3NhOVJrU0lpQklBaGVHaTlSRWJYY2xMY1JnR3FoMXp5Qi9JUHFmMUtXOXFMbzhzaU9wRUlUU3BOeks3NSttMlc0L2c4TjJLTGgvb1MyeGEyTXZGU0pTemdZZmpwYjlOK3pVeE5XR0xjZDVQR2J6STdkYW4yY2tKSmE2UkhLRG1WTWtqYW1ZTHh6M0ZKK29SdUFhTzFpcjMzWGViSGV6ZHErQjRKT2FweVhleGs4M1hIbG1kS211UTg1aWdqRmZPVElHMmFJc2FXcTdDL0ppZmdyK0pqdVRoSm55TVdyYkNSRTV2VkZ0MEdJYWlvR1NsYktIM1RYUnhaK0tXdXozZ3ErWXIzSWNrT1RIRUgwR0FERy9pc2lQSWV4bHJHNmRZaW43V20vUGVkRGVzd0t5TFlwVFMxejNzVGVTSXRFbXpZSERNRURzS0FsNVh5anZOalJvNzg5OHVhRW5pMWIraWpCc2p5b01EY0RFaHcxZzhseWpIZldrSjRwRitIWGpuMzJBak9qb0ZuNVIrcWM1ZWpsTVVxV0N1OUU0UWJTZWtWbmcwSW02OG9naWF5WVVDQTlZQklSd0Y5b3l2REZ5WGRJblhFOFFsZDRkYks2dUpwM2xzbERkclNWczBZQTdxRnBQY0MrWWdFZnBEa2NraXVjR2RINTFWaHYrMmJNWEpabmdNMTU0Sm0rcUJ4MGhOd1R0K1AyMGZ6ZlpueDh2TEM5c3RxZUxPVm9lNFhlZzhQS01TdFQ1ei9yRGQrZk1nTVB2MHd5bWVybEM5ajJ4L0tMYnRTQmtNa0hkTXZjWVY0cjhJNkFCV0tvNE50QmhTYk8wdUcreDUwbjdkOTFhb1R1ZmhrM3plajV1dEM0ZElGRmdFMkZXMmdvcldzSTBBT0huVXJadlgrYmJxYmFZUFMyT05XSW9wcW1hVVUrVXZPUzcwM2Q4WWtjSGFyaTdFREE1dVFKVG5xT1I1YkpieFk3U0hjWEVmMFZFeldpcGgrL0FKMmYvSzJUVUhoNjZZMnpqdnM4cHdKZURKRUQ0U080OEZHMEtOTjk3UzN5NEJMdz09PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjwveGVuYzpFbmNyeXB0ZWREYXRhPjwvc2FtbDI6RW5jcnlwdGVkQXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg==' } + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should return a 400', function(){ + expect(r.statusCode) + .to.equal(400); + }); + + it('should be recognized as an invalid xml', function(){ + var err = JSON.parse(bod); + expect(err.message) + .to.equal('SAMLResponse should be a valid xml'); + }); + }); + + describe('SAMLResponse with utf8 chars (default encoding not configured)', function () { + var user, r, bod, $; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback/samlp-with-utf8', + form: { SAMLResponse: fs.readFileSync(path.join(__dirname, './samples/encoded/samlresponse_utf8.txt')).toString() } + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should be valid signature', function(){ + expect(r.statusCode) + .to.equal(200); + }); + + it('should return a valid user', function(){ + var user = JSON.parse(bod); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']) + .to.equal('_98f3625b1c12bdbda1842b868eee10cdb61385b270'); + expect(user['urn:oid:2.5.4.4']) + .to.equal('Doë'); + }); + }); + + describe('SAMLResponse with ISO-8859-1 chars (default encoding not configured)', function() { + var user, r, bod, $; + + before(function (done) { + const samlxml = fs.readFileSync(path.join(__dirname, './samples/plain/samlresponse_explicit_iso.txt')).toString(); + const samlEncoded = new Buffer(samlxml, 'binary').toString('base64'); + + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback/samlp-with-ISO', + form: { SAMLResponse: samlEncoded } + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should be valid signature', function(){ + expect(r.statusCode) + .to.equal(200); + }); + + it('should return a valid user', function(){ + var user = JSON.parse(bod); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']) + .to.equal('nameid'); + expect(user['Email'].trim()) + .to.equal('test@exåmple.com'); + }); + }); + + describe('SAMLResponse with ISO-8859-1 chars (default encoding configured)', function() { + var user, r, bod, $; + + before(function (done) { + const samlxml = fs.readFileSync(path.join(__dirname, './samples/plain/samlresponse_iso.txt')).toString(); + const samlEncoded = new Buffer(samlxml, 'binary').toString('base64'); + + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback/samlp-with-ISO-explicit', + form: { SAMLResponse: samlEncoded } + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should be valid signature', function(){ + expect(r.statusCode) + .to.equal(200); + }); + + it('should return a valid user', function(){ + var user = JSON.parse(bod); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']) + .to.equal('nameid'); + expect(user['Email'].trim()) + .to.equal('test@exåmple.com'); + }); + }); + + describe.skip('SAMLResponse with signed assertion and "ds" prefix defined only at the root of the SAMLResponse', function () { + var r, bod; + + // samlResponse was not properly generated + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback/samlp-with-dsig-at-root', + form: { SAMLResponse: fs.readFileSync(path.join(__dirname, './samples/encoded/samlresponse_signedassertion_dsprefix.txt')).toString() } + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should be valid signature', function(){ + expect(r.statusCode) + .to.equal(200); + }); + }); + + describe('invalid SAMLResponse in POST', function () { + var user, r, bod, $; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5051/callback', + form: { SAMLResponse: 'foo' } + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should return a 400', function(){ + expect(r.statusCode) + .to.equal(400); + }); + }); + + describe('samlp request', function () { + describe('HTTP-Redirect', function () { + var r, bod; + + before(function (done) { + request.get({ + jar: request.jar(), + followRedirect: false, + uri: 'http://localhost:5051/login' + }, function (err, resp, b){ + if(err) return done(err); + r = resp; + bod = b; + done(); + }); + }); + + it('should redirect to idp', function(){ + expect(r.statusCode) + .to.equal(302); + }); + + it('should have SAMLRequest querystring', function(done){ + expect(r.headers.location.split('?')[0]) + .to.equal(server.identityProviderUrl); + var querystring = qs.parse(r.headers.location.split('?')[1]); + expect(querystring).to.have.property('SAMLRequest'); + var SAMLRequest = querystring.SAMLRequest; + + zlib.inflateRaw(new Buffer(SAMLRequest, 'base64'), function (err, buffer) { + if (err) return done(err); + var request = buffer.toString(); + var doc = new xmldom.DOMParser().parseFromString(request); + + expect(doc.documentElement.getAttribute('ProtocolBinding')) + .to.equal('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); + + expect(doc.documentElement.getAttribute('Version')) + .to.equal('2.0'); + + expect(doc.documentElement.getElementsByTagName('saml:Issuer')[0] + .getAttribute('xmlns:saml')) + .to.equal('urn:oasis:names:tc:SAML:2.0:assertion'); + + done(); + }); + }); + + it('should have RelayState querystring', function(){ + expect(r.headers.location.split('?')[0]) + .to.equal(server.identityProviderUrl); + var querystring = qs.parse(r.headers.location.split('?')[1]); + expect(querystring).to.have.property('RelayState'); + expect(querystring.RelayState).to.equal(server.relayState); + }); + }); + + describe('HTTP-POST', function () { + var r, bod, $; + + before(function (done) { + request.get({ + jar: request.jar(), + followRedirect: false, + uri: 'http://localhost:5051/login-http-post' + }, function (err, resp, b){ + if (err) return done(err); + r = resp; + bod = b; + $ = cheerio.load(bod); + done(); + }); + }); + + it('should post to idp', function(){ + expect(r.statusCode).to.equal(200); + expect(r.headers['content-type']).to.contains('text/html'); + expect($('form').attr('action')).to.equal('http://localhost:5051/samlp'); + }); + + it('should have SAMLRequest input', function (done) { + var SAMLRequest = $('form input[name="SAMLRequest"]').val(); + expect(SAMLRequest).to.be.ok; + + var doc = new xmldom.DOMParser().parseFromString(new Buffer(SAMLRequest, 'base64').toString()); + expect(doc.documentElement.getAttribute('ProtocolBinding')) + .to.equal('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); + + expect(doc.documentElement.getAttribute('Version')) + .to.equal('2.0'); + + expect(doc.documentElement.getElementsByTagName('saml:Issuer')[0] + .getAttribute('xmlns:saml')) + .to.equal('urn:oasis:names:tc:SAML:2.0:assertion'); + + done(); + }); + + it('should have RelayState input', function(){ + var RelayState = $('form input[name="RelayState"]').val(); + expect(RelayState).to.be.ok; + expect(RelayState).to.equal(server.relayState); + }); + }); + }); + + describe('samlp request with custom xml', function () { + var r, bod; + + before(function (done) { + request.get({ + jar: request.jar(), + followRedirect: false, + uri: 'http://localhost:5051/login-custom-request-template' + }, function (err, resp, b){ + if(err) return done(err); + r = resp; + bod = b; + done(); + }); + }); + + it('should redirect to idp', function(){ + expect(r.statusCode) + .to.equal(302); + }); + + it('should have SAMLRequest querystring', function(done){ + expect(r.headers.location.split('?')[0]) + .to.equal(server.identityProviderUrl); + var querystring = qs.parse(r.headers.location.split('?')[1]); + expect(querystring).to.have.property('SAMLRequest'); + var SAMLRequest = querystring.SAMLRequest; + + zlib.inflateRaw(new Buffer(SAMLRequest, 'base64'), function (err, buffer) { + if (err) return done(err); + var request = buffer.toString(); + var doc = new xmldom.DOMParser().parseFromString(request); + + expect(doc.documentElement.getAttribute('Protocol')) + .to.equal('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); + + expect(doc.documentElement.getAttribute('Version')) + .to.equal('3.0'); + + expect(doc.documentElement.getAttribute('Foo')) + .to.equal('123'); + + expect(doc.documentElement.getAttribute('Issuertico')) + .to.equal('https://auth0-dev-ed.my.salesforce.com'); + + done(); + }); + + }); + + }); + + describe('samlp request with idp url containing querystring', function () { + var r, bod; + + before(function (done) { + request.get({ + jar: request.jar(), + followRedirect: false, + uri: 'http://localhost:5051/login-idp-with-querystring' + }, function (err, resp, b){ + if(err) return done(err); + r = resp; + bod = b; + done(); + }); + }); + + it('should redirect to idp', function(){ + expect(r.statusCode) + .to.equal(302); + }); + + it('should have SAMLRequest and foo in querystring', function(){ + expect(r.headers.location.split('?')[0]) + .to.equal(server.identityProviderUrl); + var querystring = qs.parse(r.headers.location.split('?')[1]); + expect(querystring).to.have.property('SAMLRequest'); + expect(querystring).to.have.property('foo'); + }); + + }); + + describe('samlp with signed request', function () { + describe('POST binding', function () { + var r, bod, $; + + before(function (done) { + request.get({ + jar: request.jar(), + uri: 'http://localhost:5051/login-signed-request-post' + }, function (err, resp, b){ + if(err) return callback(err); + r = resp; + bod = b; + $ = cheerio.load(bod); + done(); + }); + }); + + it('should return 200 with form element', function(){ + expect(r.statusCode) + .to.equal(200); + }); + + it('should have signed SAMLRequest with valid signature', function(done){ + var signedSAMLRequest = $('form input[name="SAMLRequest"]').val(); + var signedRequest = new Buffer(signedSAMLRequest, 'base64').toString(); + var signingCert = fs.readFileSync(__dirname + '/test-auth0.pem'); + + expect(helpers.isValidSignature(signedRequest, signingCert)) + .to.equal(true); + + done(); + }); + + it('should show issuer before signature', function(done){ + var signedSAMLRequest = $('form input[name="SAMLRequest"]').val(); + var signedRequest = new Buffer(signedSAMLRequest, 'base64').toString(); + var doc = new xmldom.DOMParser().parseFromString(signedRequest); + + // First child has to be the issuer + expect(doc.documentElement.childNodes[0].nodeName).to.equal('saml:Issuer'); + // Second child the signature + expect(doc.documentElement.childNodes[1].nodeName).to.equal('Signature'); + done(); + }); + }); + + describe('without deflate', function () { + var r, bod; + + before(function (done) { + request.get({ + jar: request.jar(), + followRedirect: false, + uri: 'http://localhost:5051/login-signed-request-without-deflate' + }, function (err, resp, b){ + if(err) return callback(err); + r = resp; + bod = b; + done(); + }); + }); + + it('should redirect to idp', function(){ + expect(r.statusCode) + .to.equal(302); + }); + + it('should have signed SAMLRequest with valid signature', function(done){ + expect(r.headers.location.split('?')[0]) + .to.equal(server.identityProviderUrl); + var querystring = qs.parse(r.headers.location.split('?')[1]); + expect(querystring).to.have.property('SAMLRequest'); + expect(querystring.RelayState).to.equal('somestate'); + + var signedSAMLRequest = querystring.SAMLRequest; + var signedRequest = new Buffer(signedSAMLRequest, 'base64').toString(); + var signingCert = fs.readFileSync(__dirname + '/test-auth0.pem'); + + expect(helpers.isValidSignature(signedRequest, signingCert)) + .to.equal(true); + done(); + }); + }); + + describe('with deflate', function () { + var r, bod; + + before(function (done) { + request.get({ + jar: request.jar(), + followRedirect: false, + uri: 'http://localhost:5051/login-signed-request-with-deflate' + }, function (err, resp, b){ + if(err) return callback(err); + r = resp; + bod = b; + done(); + }); + }); + + it('should redirect to idp', function(){ + expect(r.statusCode) + .to.equal(302); + }); + + it('should have signed SAMLRequest with valid signature', function(done){ + expect(r.headers.location.split('?')[0]) + .to.equal(server.identityProviderUrl); + var querystring = qs.parse(r.headers.location.split('?')[1]); + expect(querystring).to.have.property('SAMLRequest'); + expect(querystring).to.have.property('Signature'); + expect(querystring.RelayState).to.equal('somestate'); + expect(querystring.SigAlg).to.equal('http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'); + + var signingCert = fs.readFileSync(__dirname + '/test-auth0.pem'); + + var signedParams = { + SAMLRequest: querystring.SAMLRequest, + RelayState: querystring.RelayState, + SigAlg: querystring.SigAlg + }; + + var verifier = crypto.createVerify('RSA-SHA256'); + verifier.update(require('querystring').stringify(signedParams)); + var verified = verifier.verify(signingCert, querystring.Signature, 'base64'); + + expect(verified).to.equal(true); + done(); + }); + }); + }); +}); + +function doSamlpFlow(samlRequestUrl, callbackEndpoint, callback) { + request.get({ + jar: request.jar(), + uri: samlRequestUrl + }, function (err, response, b){ + if(err) return callback(err); + expect(response.statusCode) + .to.equal(200); + + var $ = cheerio.load(b); + var SAMLResponse = $('input[name="SAMLResponse"]').attr('value'); + var RelayState = $('input[name="RelayState"]').attr('value'); + + request.post({ + jar: request.jar(), + uri: callbackEndpoint, + form: { SAMLResponse: SAMLResponse, RelayState: RelayState } + }, function(err, response, body) { + if(err) return callback(err); + callback(null, { response: response, body: body }); + }); + }); +} diff --git a/test/samlp.tests.js b/test/samlp.tests.js new file mode 100644 index 0000000..302a14a --- /dev/null +++ b/test/samlp.tests.js @@ -0,0 +1,792 @@ +var expect = require('chai').expect; +var xmldom = require('@auth0/xmldom'); +var fs = require('fs'); +var zlib = require('zlib'); +var server = require('./fixture/samlp-server'); +var Samlp = require('../lib/passport-wsfed-saml2/samlp'); +var Saml = require('../lib/passport-wsfed-saml2/saml').SAML; + +describe('samlp (unit tests)', function () { + describe('extractAssertion', function () { + + var samlpResponse = 'urn:fixture-testurn:fixture-test12345678https://auth0-dev-ed.my.salesforce.com12345678jfoo@gmail.comJohn FooJohnFoourn:oasis:names:tc:SAML:2.0:ac:classes:unspecifiedYkV3DdlEa19Gb0eE3jTYTVPalV1kZ88fbIv4blO9T1Y=ZiINpNlahQlp1JbgFsamI1/pZ+zcPsZboESVayxBMtrUBYNC4IG2VBnqku7paDxJQ7624CvcNzAYWYCv/2/c67Bv6YhQwK1rb4DPEL6OvbI8FNkYAhTNNw5UhUTEMjnJ7AncV/svUTYyIOyktuCvQh3tR4teZJV+BM3IKj9vRQQbCRNSUVHJEe963ma5HcCyo+RhIKU1pm4+ycswOlY9F115roKB4RNRJLs7Z5fyzhbOoCUujR9MMKHHq+CWaYvh5SkjaH1wMorlPlJtq5dhTZtDRhj4HwxYpCG5b4NF2vp+Jpni4dDFKou0Lzk0k6ueCJGcNHfidfEB3RB20Hed2g==MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo='; + var samlpReponseWithEncryptedAssertion = ' https://adfs.fmi.ch/adfs/services/trust CN=fmi-test.auth0.com 17575331292066593326 PfbLmL7Eb2NL5lzxyuEuKolgDtHjAuVDV9AaDKp1UqxSKXPGQaF3FTFt7Gl7FRmEdfjMD4xyMII4c6itasV7/N1WIXVw6j9VpvBZ2WPH4lT2gHVXEnCSko+rlk0OfFQN/XFY1HZrPb0PeSYtbuR6Fe2KDLVYYSElrGyn9lbU/zgLB+cV3OiidMamymcTjBYxr3+wv8zhEl2jDYd/04wULeDydhNpA8KFzjy/DQwE4GwlOfuCCtZboai1OXA3++KEuwH2QrC5lRpmnpwV1OJj+ozWDmJrRLA/vpxakQfMzjBcMoBx5wy0dvDaXcjMZk2aaOUhydSC+vd5UYD3npVlog== AZX6tZLTBQJTsrbXGj1QaDf9ZnMigNI3ySiH7deoK0y0M9gkzC6+7tzie7IbavR9QkdLlB0NCnokPFYyxS/w3NsCT3qDk9o45f4LNVqBel1sVagG1rNFcjMsH17V0Phj5idh/acvIx8s22XDC44XXeo0/FT3ZC1HPBBwS+c4UAFI3OiYux61gzA4zg72iZoqs9Wt6ZpJdKn3QtrOCYGQmKrO6lKzgHLkHgB4Lk8Th24OqfeRWdau4j91z28gZ4teSlp9oARgXrrGjdFneXivTSTdDfMwKOmCr5eVfu5jUBCjeaL5DEU/mlpfUwvnQQVOq+rYimq4+Yp1eRXr69diRJ73Ne+7iL7CzqXDLoYuz+ZMdGE2hEU7L0nn1mnmPaGtbdtL92bj2dALNeshWJjjBw7Qem/GjJFEHKzsd1OfhMRuNlfpw6gFku3/+QcYac/FJYzxOIfEzKOQWL8GMLm96CZ0J2Par2yEM9oi4fDtRocjyAhX//JSgiB5HDS3kxDc4HNGSqOgmmXGi6vQR9+82fIlnRP7iO1xD6o61sKHBOMI22bMKouyx0XlKoNHPuPMQGmHfgbty66KFgqkLih5nLX3TzqNommle9ZwvcIvgZ2PWRmiLVtfW/Yc3584zp1CzF/VwqOXCalTkgEObuSXEODU4JpGVAViPCVyriVBu1kWmws/kpfaTUe+brI1m+hp+0tpjKVh+VoesXR+9iPMOHbX39Slmah6zcjU/UjQAN+rtF3SSBMrRd1Fc9VD2fevvD1YvPU9LUAo1BkS7e6ig0jcsX4TC+tdNR+wWiNPhYclIuo06Nd4Uk1f/WkdV1+cRDIobdVabiq6EXSbaJAzbCepCJcOn8dNr0301Os4SIi0EtEQO3wk/Sx7e2UVlmRofK8R3p8TupyO0skMhUzRmlFmsI7kFpKUfcmtshamt9JVN8qIQCowxPgRmy0T67swJgBFdRX5C34CXxNJvGw8Eld7TDoiuQa4FxN2T7ebjaAsBQGYxsBPaGQQFNFTptGNsC+2YDFKV82rftCSyoZiAg3wnz/qjcsB02TOIGtu2I9M/lspl+N4Cb8adludm+YnfK6yRIUFzx2Y7N3hh4WKwvfK8IJuckg+dKC3IOyW2L0dUTScUNB9nB/2jxYLXyiqyT+B+/83BVRBjitFw3F8web3i8iLmMFJswnbL3ONYzUbW7Gu67y+LSHo3yRIneVCJrj91ihvBUMvae7kgoQUVj4vYFMPsykJaFypb59OXe6CyE1bAOHcKnPLRC7tix+TeSgQhHMIqr7yPZXHEhX3FfduxsrrnN4QYIqJOYlirqTh0SdwpT7Y2W02iEdEDBNyJs7kKH4ArRUrSu8xFi/vaNMB896lRy+hMAxdtM131MRV+eY68rNhAb275a3cpsYONRJPym4CRegV48rr6yFHm1vhMoXo2eNBIoQHm4wUInxwhYw0yt/9WM2AU3UwIOdCTHwJQeLWgJu4PDA4O0Tmrm2bS4kFEM4ya3Y6KXhjVHyoxkHzi+PYVNzEKdobhxOP2+1n/5+/SU84+WqcsQxRtoXFloEr1GMSt9L1di4w9uuzYngM49P63CQBMQVi8hz4fPrkZzm/V3MwZ7aOIm9/JTr2IPeuJYE7LHh3VDB2uirFGfrooHncOKDQfAqgSrAF7ztSYgY3DDuBcBMQ3uS8rMqrH0Uwza1hF7p+7dfUZyzt7OF9zGBJmOWK2YLkCL+QiCxJMTG+til3AyHwRVmACdL6uNmBsd31Sr673YiFaPTZC2Q6wu48HYZQ0z5qJwOpBm5EHDuVDCwT/GqkTwQD5182f5jQKX5eWIa9gehuKWrTfOZc0DU93yfE1ZGXJq27RrAv4Lzfh59lRvasGL3PZ+rRLuALgKQ5vBgJXlgk1T/hHP9sB1BAG8OpwQoQOFlx4y8kZLzxQmtRBb9BaTzl43CYLhsXgPBsepRSL3RAyG123LgDRz56TU/b6v8Wuu/GzkC7Afr237HazCiRG/kpKqYAEEWKjHPVzKFnJpF1EiuaNxBncSMPc/zn5i80oS6aTT4yQ0yxyIxKBzRGipZewnn/u3qSLy2j/z6lW1vcWEk/hdjC1HQ9ya0JJDwUB5FF308S4oK1E4gTsu3uKkKiHTYQC7Hxp4XQogjujCzWH/HvW2FsA7Na1EAkIu0KpzikcNvZ5xEBbIlmGqdsC2/9ybuQMtoxxleKRT3ZBgpuQqcYDt/iQDUaS1LpWQXN+7pg1eRy/Dwzitfq1zMO1wCrFEnvGt9WCBKAvX4+s7A9YmDPhTfdpKTQRe2df4QjkvuAtMlM4DYV6JkKj0S7Z3sjPBCzqFF93HM3KvPocHokYa0s/SJVTVkRot+EE7emGoXU82i99jMpXCjsaujTrEGawFhvNX0QhsXoUP2qWAEquRGZ7eBEUhWwHSZhdKM4/HvMa8fYklhKZ4T47b+pCSkeny3ycajy/ClUDGiBLO+Q1IN0qyOWDVAPB/+EPKVct3Bx+WzV9f57fmXZ+wfXjBHYodIfX8tRbehZtLma2h+BNenjiiSWFERrUURV1l1osL+3kuEqwewc/8ys3fGhCWj0+C2hubOUgA0yCZH8KtuJVpYvR4vjnJ8C1g6QELsWgaKWXEw58kRXP/CFAVlhoklS40+HPq5SfjaDDcUOsc2qwzNp8+0ktk1ozFJx3k2fEirRoS7q2upVuN3sCLC4hduDPPMrmStgdUsLwzg1IK+aAWQgvVThmF449nVsDVZGcVeyoB81DuCI+BCKP+apJaPcjf0f083rxEbUNMnKv6GhWl/Mkyhhnafuqq80pMS6ehm27CZSk9Snh8HxI3QMH1cbIx/iHIGOA1kP6ulV4qdwKh/KXYnu/r6JkrSBWQp/21mnJ1yWLSgiJoM+zoWzBcV92Qffjj+2yLN3wdOSaxpPX2B2jU997m4MOr46ut8pHvE4bdTbVpxIhi9f2gzv36ElT5MDTXCiS5+svShCYVEoIipwEmJMs+l/HXwR7PtOvPytSwh+eSC1Z4bTdSPhdyiCMu37tAwlK0K6WbcUQfJE7cPMs+gKgAB6m4VenDV7SQwC+ARWxKACvtBU+QTGudUE7NUHsMugCBHjYB9bKtbakycEachQykDRDkkZ1PDL03ipM8d8Gb0Tm9dYwerBg7Nmw+jt69+VqCaFtpeOc+jp+e5bWEfg/HCHchsHGIQ72RKlHKLXmFwEJ7PmzOlbNGT0Usltq+9o9vHL89mfNK6n2xbneaYyKGFzu87j5+a7caSwJ7CTCFCnq6hctC5bi1tTqQM8tkBiv1lCTcy1kB1t7WX2RpkV99jfLPZPinTI4l+CtjJF3WYNSGgK+JJwzzflL1mobgdHVFGYyERBkx/FNq5aqGSkJA0dki2i3e0liQ8hsybtpe+uX9sybKCMy6MVEoMCzGJBV2g4N/OLCC+WsXPMbJ6SqyZlNKooRtJEuwZzJb2hlWx2298AxtNTcWA+u+gxibf6GZlGcujjf2+/uUrzXRw+hBQU0O/wD5pLvXwYIuxElo02gG1XQu1RLM1qrGg5ouEg7I5TmJzOIjIhhvuGEEoQjZMqA3byJCzj9a7LBJ5ddbmB8Xp9louyJbOuJghgt835r7PfPIqA+58UqNYWkFHYt+PjPc4+DPetjKi0SmvmtxVGjM5qsRCmiabBcY5nHQGNaGIsmg0VsaauBe78LjKLnJL2IR9wgNEEBadyuWHbNtd7wsf728+o6PQzEU8LJO5DK5QvxbX3QVyZTi17nkykvsU0nVqiYZ8Wyc65XgvDjOe18ECG9xeX6vd3pJ+15we9xNJmvRvWy/RCDSt0ul9hUJiHyXRjZGxkP0VD5bWM6MO3RcWmlHwaZRP/U+7sGY1nqhYp6iBfGgKeKlCIwQLeS/n815CVCxHkhW3Vf6dANBirojH96kvcpWLKq/DaVMlmPOkHy+14kMQtZTEqgHuo0Sm/nS5ddVVj1VcFkI+CqK45a5u6Mf/EB/TTOTWTY+iRbwD/grQ2uTRmUMs3G3Fww2xtu1N5jkqm62ooU8CSkr+zVuskX1qRlavV7Z+viDbL5XGiicKf32AYL/KZSLpthpD3Y5FIDuMMXn7xlXSnXDZxbWl9GF4DeMA0pgOlT0afRo+DyLLNv0ot51w8UCX200rPUeI/U/xPaBNOXFrAvlS6syp6nG3ldiJsJMFCwtxb7vO5tSKpQXUXJ2zOnYhQjO7Ofbyfprs65ZZRCvvDh/RaCcYm0MyFmdWlTgz7cd4dkDhv8SpvhnlPoWwoUi0d9s5gxqmPWUEtVbuEMCzewU1XTcJufSP17mmF6ciofj9t6tvY588Kc0cdGMshzcYnhM6vgHrFiZQoKxE781/SBXKeNG/o6NqQYq7st8t6mwaS5Hx+1eimMCT147dnHNmkjf4TKhLw603QX9gV94owtbc38eRB8UCmgs+37J7I92Ls8W7V9sV5em/JO4K7r3cuiLBfQxkljU+cLKcvpnM65/IAAhXo88Wka3pctkejNTyo3pNbwyikekCYQ+nViSzOjXskQWbcv1ZnzbSGzQJIA7dtgDvIdvQulAveK9VQe9zpPHHomSqBzQr8cwklgC2SJoK5VH0landsMyZR/Xq/jUObUEcvd9kp+MU/OvPz61NES5cqYxCgErEzbv4jxKY9/JohKfue3f+WuQ9pwpo9AbVLTWYFl9uYvg4xM66FWcdfpSh6phv9Q8xfxZjL+2+qC0j/lD9DKB1ztHavDgyRby14iIALJHGi1t3F5JrPSib2XJL38xqIDYCX4iHKUEwwtrb51jWIhc18pLu0QNa+2z9cTrf2zPRTS2LZFbBt9RUKXhaDHTIxEKtiYKIO0fwA2xFUPZaqNfQjyLHPPo1xaOSsepf8fqZplcbphNFCxbKJ7awgZmUl1uGZ4gVo6USSjEXBPTFjJnqV+AhF484petGC05kONnLKPIZ+EQWdRBnQ/yI1T2HY3uoj0QI6qzUFJPg9ujr3KoJvPdOw2Y26PV1J6n+0iEl0whnmD5YZkhetkShbGKILVrLwjkrssuBncUMiWNHPxq19gmGoUEyNs6jWvUSIbgUDa0lsBAOffLRZnVVp0/cVNT6ba7ZnGVWQGkW8Lh6kW/nscf7gKaWoV5RQsP4jAv3GhOO72U8Xvi3v7go21NmfARGq/gTe3XWgc+d3+A0UcHxyDGiUxnr84EHi97GzWA6qtpUWMoGFKNzna/IwAb0iJVBjgJV2vou3R0tvQsHrb6k3WIiUmpIgIkVbC82BzbU3MBKnnljmRpnoUBghkLpf6jjtmteepyezSpCrnzLU3JBJnoXnvoVpst3fA/ByxMUsVpWmS7dT1nQmfifXY056vi8IjeMG0oKVrZlKwZaV2EU0vIqkm/gSgko/h7PXKv/mXDz6hhcft2MWWhEZt5b+40dahinDBhzlRvKcCzMuolDlKMoO7bAjH434ZJQ0LYZX7VSvU5obosJQeZvSE98Gmh09ylYydK03FDSqnK0s3y3Dlo4UzdPhSzJUXk6qZwRaIyReUrHx+0yNgV6JG9gYfabT96dF0mGJdrA6Eitwziot1fEjsKziZ71T4+Kdpn385fjpK1ljQE1amAxYI8Rcs56hoqpmq2QWINQnDXUPpq+jjAf7XnCX/wP9iappXeA1cIN4pTqEOr9fjJsO38gRyxPcSl9ak85+HWyfKx66TloDW/OOHS+M8pX913u/rLKx9Bwe64QCXVY9wRV9aEQv2+RPe/i91lCU5ZqFZclpsq/qcHLlMAk3CNXR/mOHc1tlGT5u7Ds12yVy3RQTLd8kh9p5b4PCdnI87Mp4cPONhkZfZYTjNUd9e/mDxDwk20YjWytDRrxC+o/N8rqEte8+EAPVjB8SsUuN/tX3Wi9mEZloR+MRLfX9jO7903MDUGAL+JOPZRzsLvllNjInQ589OUZxtfXVSVmts++2lxZ8AWtxmjZcuxu3WfF1dZ5WxbInu611Fr1tU+sm0sFFiryN0m7XQgs= '; + + it('should returns assertion', function (done) { + var samlp = new Samlp({}); + samlp.extractAssertion(samlpResponse, function (err, assertion) { + if (err) { done(err); } + + var doc = new xmldom.DOMParser().parseFromString(assertion.toString()); + var attributes = doc.documentElement.getElementsByTagName('saml:Attribute'); + expect(attributes.length).to.equal(5); + expect(attributes[0].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'); + expect(attributes[0].firstChild.textContent).to.equal('12345678'); + expect(attributes[1].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'); + expect(attributes[1].firstChild.textContent).to.equal('jfoo@gmail.com'); + expect(attributes[2].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name'); + expect(attributes[2].firstChild.textContent).to.equal('John Foo'); + expect(attributes[3].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname'); + expect(attributes[3].firstChild.textContent).to.equal('John'); + expect(attributes[4].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'); + expect(attributes[4].firstChild.textContent).to.equal('Foo'); + done(); + }); + }); + + it('should returns assertion when the namespace is defined in Saml Response element instead of Assertion element', function (done) { + var currentSamlpResponse = 'urn:fixture-testurn:fixture-test12345678https://auth0-dev-ed.my.salesforce.com12345678jfoo@gmail.comJohn FooJohnFoourn:oasis:names:tc:SAML:2.0:ac:classes:unspecifiedYkV3DdlEa19Gb0eE3jTYTVPalV1kZ88fbIv4blO9T1Y=ZiINpNlahQlp1JbgFsamI1/pZ+zcPsZboESVayxBMtrUBYNC4IG2VBnqku7paDxJQ7624CvcNzAYWYCv/2/c67Bv6YhQwK1rb4DPEL6OvbI8FNkYAhTNNw5UhUTEMjnJ7AncV/svUTYyIOyktuCvQh3tR4teZJV+BM3IKj9vRQQbCRNSUVHJEe963ma5HcCyo+RhIKU1pm4+ycswOlY9F115roKB4RNRJLs7Z5fyzhbOoCUujR9MMKHHq+CWaYvh5SkjaH1wMorlPlJtq5dhTZtDRhj4HwxYpCG5b4NF2vp+Jpni4dDFKou0Lzk0k6ueCJGcNHfidfEB3RB20Hed2g==MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo='; + var samlp = new Samlp({}); + samlp.extractAssertion(currentSamlpResponse, function (err, assertion) { + if (err) { done(err); } + + var doc = new xmldom.DOMParser().parseFromString(assertion.toString()); + var attributes = doc.documentElement.getElementsByTagName('saml:Attribute'); + expect(attributes.length).to.equal(5); + expect(attributes[0].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'); + expect(attributes[0].firstChild.textContent).to.equal('12345678'); + expect(attributes[1].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'); + expect(attributes[1].firstChild.textContent).to.equal('jfoo@gmail.com'); + expect(attributes[2].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name'); + expect(attributes[2].firstChild.textContent).to.equal('John Foo'); + expect(attributes[3].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname'); + expect(attributes[3].firstChild.textContent).to.equal('John'); + expect(attributes[4].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'); + expect(attributes[4].firstChild.textContent).to.equal('Foo'); + done(); + }); + }); + + + + it('should throws error if EncryptedAssertion is present but options.encryptionKey was not specified', function (done) { + var samlp = new Samlp({}); + samlp.extractAssertion(samlpReponseWithEncryptedAssertion, function (err) { + expect(err.message).to.equal('Assertion is encrypted. Please set options.decryptionKey with your decryption private key.'); + done(); + }); + }); + + it('should returns decrypted assertion', function (done) { + var samlp = new Samlp({ + decryptionKey: fs.readFileSync(__dirname + '/test-decryption.key') + }); + + samlp.extractAssertion(samlpReponseWithEncryptedAssertion, function (err, assertion) { + if (err) { return done(err); } + + var doc = new xmldom.DOMParser().parseFromString(assertion.toString()); + var attributes = doc.documentElement.getElementsByTagName('Attribute'); + expect(attributes.length).to.equal(8); + expect(attributes[0].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'); + expect(attributes[1].getAttribute('Name')).to.equal('urn:oid:0.9.2342.19200300.100.1.3'); + expect(attributes[2].getAttribute('Name')).to.equal('urn:oid:2.16.756.1.2.5.1.1.4'); + expect(attributes[2].firstChild.textContent).to.equal('fmi.ch'); + expect(attributes[3].getAttribute('Name')).to.equal('urn:oid:2.16.756.1.2.5.1.1.5'); + expect(attributes[3].firstChild.textContent).to.equal('others'); + expect(attributes[4].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'); + expect(attributes[4].firstChild.textContent).to.equal('Pan'); + expect(attributes[5].getAttribute('Name')).to.equal('urn:oid:2.5.4.4'); + expect(attributes[5].firstChild.textContent).to.equal('Pan'); + expect(attributes[6].getAttribute('Name')).to.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname'); + expect(attributes[6].firstChild.textContent).to.equal('Peter'); + expect(attributes[7].getAttribute('Name')).to.equal('urn:oid:2.5.4.42'); + expect(attributes[7].firstChild.textContent).to.equal('Peter'); + done(); + }); + }); + + it('should return a decrypted assertion when using tripledes-cbc', function (done) { + const samlResponse = 'http://localhost:8080/simplesaml/saml2/idp/metadata.php QTrX6jHzUq7YJmrQHHfY+sPH7IA=fT3MIZJgBM+2i8wMrZNBbe2fkEBKbK1ojnRqgaPvZDrWyPQcDY/bkhaF954nH+n0ZJud36beqSlCzEmxT+OF/MOwE2oEgqWavRYvTRpIvErECvbHao7S+XD40fnqDWoycDZRFDaMX5/V5S+Z5cDa7Yuou180OOdqlxewNfq87zM+q085griRl5TwwxISN1NIFa4tz7mDfLii3jNiLB6H8TPAbRhQ5qEA2R3pY/Q7/WZ3pPUCGoXayKFXEnQ0YqQZw3RgPPKVpd7t2fH15tc/1pYlqsv+3PfR8TlWB6AouUal74jBWA4BBZOzCzSOAGI2Wu+HatSW7FTbbBnxOv9y+A==MIIDXTCCAkWgAwIBAgIJALmVVuDWu4NYMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMxMTQzNDQ3WhcNNDgwNjI1MTQzNDQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUCFozgNb1h1M0jzNRSCjhOBnR+uVbVpaWfXYIR+AhWDdEe5ryY+CgavOg8bfLybyzFdehlYdDRgkedEB/GjG8aJw06l0qF4jDOAw0kEygWCu2mcH7XOxRt+YAH3TVHa/Hu1W3WjzkobqqqLQ8gkKWWM27fOgAZ6GieaJBN6VBSMMcPey3HWLBmc+TYJmv1dbaO2jHhKh8pfKw0W12VM8P1PIO8gv4Phu/uuJYieBWKixBEyy0lHjyixYFCR12xdh4CA47q958ZRGnnDUGFVE1QhgRacJCOZ9bd5t9mr8KLaVBYTCJo5ERE8jymab5dPqe5qKfJsCZiqWglbjUo9twIDAQABo1AwTjAdBgNVHQ4EFgQUxpuwcs/CYQOyui+r1G+3KxBNhxkwHwYDVR0jBBgwFoAUxpuwcs/CYQOyui+r1G+3KxBNhxkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAiWUKs/2x/viNCKi3Y6blEuCtAGhzOOZ9EjrvJ8+COH3Rag3tVBWrcBZ3/uhhPq5gy9lqw4OkvEws99/5jFsX1FJ6MKBgqfuy7yh5s1YfM0ANHYczMmYpZeAcQf2CGAaVfwTTfSlzNLsF2lW/ly7yapFzlYSJLGoVE+OHEu8g5SlNACUEfkXw+5Eghh+KzlIN7R6Q7r2ixWNFBC/jWf7NKUfJyX8qIG5md1YUeT6GBW9Bm2/1/RiO24JTaYlfLdKK9TYb8sG5B+OLab2DImG99CJ25RkAcSobWNF5zD0O6lgOo3cEdB/ksCq3hmtlC/DlLZ/D8CJ+7VuZnS1rR2naQ== MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo= D1uLLnF4HXthsdJyhNxfEo+hyzCAqunXKYFfNEpgd10+sE54lmIG6Db4aKz1Gd+VgKaw5Qigjd0pxCJVHKxIXtToR8Wp//t8BRBvqp0UVwdqokl+dJwYUo4aliqukFT3pXl4Z67nsuecpA8nSIbFSirUN3gfMAg47GirFDa7DVpIOu0u6YqH6ZcIRG/QihBx5ryIN9PwQLjaZmbZfthvugFWhjTt+APwHn+V2TyJgPYDknD5NJ6Xj6EO/EtOENpSMb4loIV0PonGut6be8hCbq7ShDuTzyqoP/HahssZYpYS0u7bOs1sV9oSbrp8GAYrZCEVVrN+1TAaXgwSRYDwAg== kTOEFGnqzo6UBLryqesUkkV622zIlmh3rmdVvntbBkJo2NumTHBbWnVcfmOFEk/UGSMONuY++cYg1Sn3y2u5xqzS6yGH1fszDPY4zcNXfmnT08coVipX2g8ILd268AEIOb6fm+JoqUYCVv3zMwn70BmQPY9MaGJXCcoeG1trWPDf6rdkdlsNnxdXNqgNQ7OphuaQEHRo5YYfQ/QDNfxXbtWzJ3JjwKdq1QWe3o+fIFWJlgPCv1LIR98Pu5pcTwMBPCPWcTdgoAiBCEvbcB4W/Ry5Y9nc1doqfSuHaSouGbq4TTWqw2HjvmkUjnNcYuVxAf4VaRHJZ6leHFXk/fd1fk3QyK+MrzKGFDu2WvKW7NZH6fsCnnGL93f1aXToyhagiz/sGWzMBlFN7OAuaVi8IvWuJ2qlJIc5SGYQFX5P0yFPKxkks2QrP3XbavfxTE1UxPtLeRqPcU2qHyHp1eO5vOwQ5EDqFAi89vNhDjkAaGuP8d4XwkwlNuz/c75jI/gFZqLy5Sy+PeSXggvWldF83nEAqu11bDXI7pX9dDP78yYwfUQJE9Rp0JesxQwqAzPe/jyTbpbLBxpxXZt/4W9F3a+02zM1vki9nZQkNIKhYKufZ7D8D7oGcswcEP7OfcqZooUyaM4uiQQI4gOAfrhQAqEPngFr8oLhp5UgfEDV2Ugxm9RzWvxYuFnpNv6Zd4ZrJaF9MUkeo4a+f/Fw1qPzX5SBRWSwJV48pxDkCXgBErtoZ6Jt+NhODMpUmXGbkp3qsRWlHEFsUE6ED+kqyZ9DhMkRJ2m0dDPDUUNQ7n2SS2+H6gs4lOoQ/vX/ayoAkIM5Mvn0eJIg7ZzL8ENkFvFH0Yx2srRb7RWKTmZ8qUZ2B1jRZiUCeXNFgJoW/nG1QH8MfZANPrZyNO7wBuHLPXC44SEOfuWg/GcJSAX1gPHV9A5M0xq28oIGkk+2f6YV36ShvWMGhMx316648vXP6v4liTZKY2JeuxltgKrVLU84ppoPVxAaHwjQ7vItCcROrFU38O9Ax3lLbSFEqKfi9Mr1UTtZ2PALUK9po8B05zcUd87fM1XjzEm8LSBtLou5pP9ylbGjGadaxYeHm0/lDSV7wM84eNV5dkOkLd969h6g4SAXrmJQvH771Y5ksDpnFGEx5hm1WMrvPyeOgNX/u9HeOvT+CWkcZFk3DFDrlhbOImMRu1ePVnhkBmW6sDYZwcZcR6xu9JiMBydjgNufBjnZhOMq+7gDYRGXvfGkza9/D79DdHRAUqX2gHdr8qxGywDOgrn0ShY+RN59fng8ksyuSLigHCKjmeda/0sINWjwKqEJujTTUDt0IlZQLqD5JzGInRU306bQ5kG0/u94pb4QyomMfbgDU5KyKRYG8RZQMm2uIH+Edf1zcZJ1ZYb8iVghkGEWTZFq4NU/WGTiDWJGRwvEIZb2suTpNPiT9Hw76OYGVGMmuQhA6uNYjau2dTbbltmlkTuqQrlmpAOQ5ViTSTSDKZ66oPbG+28pzwUsqGrg65+6yse3nJ6NZxEVQjNfbxyV54EpOVBmLDRiHdam0rbOerpOxX8QgLWHQKNRHGBLPCvsAXkx4MBk+hS/jg49GXb/2g/iavgHDSuA7FCEyfJHa34Fz/aCfgJfXTWH8Jwk16a4VbM+S9rkt613/2QXlaQewhfdYWXVVwxsfv0Ng9+/nHEDllWEl4ZRVLhAKcqeEDsQhPkEhRn1pAAP1LPR2YSMVnug40tLIorOTa4LYe7nL9J8ABEhXk7ySfZO0K2tDWVnB9bLbimrZOdZJPyHBuI3ElweitbdRUBmf6RBDlnb4ctPvUQ+smvRy3+GkG6DlKTFxgwk2JqmLDmJE9tIPcpIDNt0lP7D6D/6z+CQHD/H5BK5n92QItXSPVQKj8LLoGxjEEas685zw41j6tV9LwIdVfXtLhmL/lbN67rxS7jWLG4wy93kHRHN0G7K8TVdB5UUkii5S7pgB5MfiJuaQ19tNWEpXN5bXO2MTjUpt4KPCnsE9ag74q9PMoaAFxY2d6M7LV+9z2uy/eeXgN/C/qBh98PzE/3abDdWhcB6Tj2lZZMjDliB1saldF9JTg+xCdLd+ADuLCqObwRFN9IlrJLeS2RPKmVHTWP2nHM5fuu9Iws7B8Sl74ur8ltEFBtJ2xGshlBQIhS5xJLkIXpdGLnx6YYvBD65ZI6lR642clCW/KC+rXFPZ0o8E+u6AttBkIJK7kVROhHNykYMn0zvH8gCoyR3M9/QqPCfQY8+9SMKS9E3vvStaR7tIXSr6lrMxah8U2HT1sD7KewVwSz4fsxzD2dLYH69wjP5XK4WKfrnFX0K1AMcZGAk69mkcN57/cZhkYh6/7IEebF0T/MCkulk4sYyJc/J9IbsNrRF0iMEndxJ60VvZaEydLlMpkU+lPTgUCtbwHh0bNiHjSw8gjGNjwmFf3pwl3OTtR6rDYtOWBDNmukVbpF2bXqcXFEKTr6LZyM317wi+j9VxRWL2It7c717hFmEUs/eyqTXz3AShCVWMsW9cdJh4+mIHIxr4v3tL/o4PiBK6EBdoJtmEFVVKs0Re9FVhLDgpDYz3uafmql5osagiIYkFpNVg2F5PID2ieUhr6BsvZ0zZmirmcKul5uLE7/JGUyeLCDLqui/f0DvUlqRyzIMGl2BJvKFVxfRRCLo8mo55LaC9+CtVL8PW2p8RCIpDuNCigH9F0CRjztyXIi2Hu8dyBPGFcy5c10PP+8vd7Bg771QlccliLICZCvC9eg+LgGLcONs5jJnI/stboUQHJJ/5QBx6Ng7aO+p0Iqyofo0+bPdXV/SWMv9Wx1klwUEpnuUkJsEvOLfancZyqo3GRU88hJRWkLePJesmHrG8MNH8H+Sgvq56rfzQ7m0u9hqcyK6HBfdkxD6bvmVRQ9iv7RlFeXO1NZGOrkVrkfGoq2L7pwWFiVg8PfJ5ftWloeweC9uWktd7LsIWOI96mhqQ9txThF1zw0tZm0/FIrVaE7ZiXcn55cHyrsyUm0YJP81Xr59DoZWTRyu7CX8Vk17Tqla7fl6KZKCPyD/DPV3yItaiUFmSNPk+rhkft+LJ+B/Ry2LqcrV/UvJkMREGzNg4MeazWdRZXsJ8mX59ETjJk+dKc5pVp3gWLKKjy7nz4u8cHUpt/BtLiNZMw5IMn8rQL70VWuD25gPMTnLQqWYzUfnAjCVh2G5KMJ/odfDDL9t9sB5A6j+zqN1LZF7W6uhoRj63WVGvweus+l2OWvzvGp2+ki8HHfves9dOYpKM2V3VRkF3tsse7st+WjCkcjPd5h8fRgbaY9CDEwF3gvtk5lCJ7Ym4Kf5TW5IG0bDSqrcwPEHfsPkyChVJtzWkGKNSjmPta85VkZUBBRfVF3bfytLFSoCyeUMNY/jw42sjrEBusf0tOWMTtvmlbyZEd0dfei8hh4ifzguQoF0jDyjMUytpa7FgyHADcKh/xR9g7g7+kSOU/jTZ9BA8dMp9/q1i0aXCXCA/k5K1Hay+VdRQ6tKaXTrIL50wlxoyB7kLqyW+3vEWqcAHzNuaUOF7RYHjDHZnjM83wHo2HAdBuBHUI1a8pqaTN5vgHB9B8cALozaXfTXQzdK20SOpzjm27sD8ZgAZr2rGcBW6fTr+MPKENSkfPHTwH8DeNMRE9ZZlh5sr0aQmvvP07TZjk+jdd2mQUVdicN4OH7Y+uQTyTA7ajzk6hRwGq/LTVBR9xMzDyRtlf5ihTAE9b91/vuuL+rT8CH+7vjgQUZGFnEa04hlZRFtDHBA746YVxDJUFt1ryxrRohVjYqXd6BL2Qe665uGcTz30NiW7wdCQsl08LzDpWCMdAlwTfLU9j4UyO4JurJJ36pZdiX1RsBCXZDsOv2dWVRINS3mLwErRC9UfsEqc5zRZOiP2Uw+WN6hoxc5Gl/ZuPPkCS138j0OKd7V22Q5ADPvdFpkGDRByJ/EmTNl9buFAm7yg/Wq8ojivrnTDcku6OvYGL9EyneyqPNL44cXFQy1770TOpN3VyDORp4NAUURx43GuB9WCrMcjRWVGfG/zihjFRAhRzfjvJBOhZ9pnwwUHdR3JHNXHo/3hCJzhAvDsnLq8U8QSILnsXtfGiasKA2OssNzTelkigf3Lv7e9C5qAhi8FgR4ZmjajTij1+1t8fzp2nr0Ut/refJuOFS3egXHlBbSVKdQqN5ZG2an7Fiex7jTw+YCQCw6Ip4BnuFPPrdseker1P7BD4YiSJF5yMG5WAxRzpLIqVtWflEnaEbO0tmgVhSFtONo9oONMS1psYGni6aQ5R2DE/p6W38q47BqGxwRa14zLkthuN2vPIzQLyD2x8QJCgF7ptv1kuahiv6JI/lAhEQNKPS2gd5yC6w7zFKAjUpv77g5TGlqBXZEuWGKom0zFzU8BJ6bvEn/BZoGR/MXgRIwBtIzDDSnPmlG5SCA7I6n7JcejP7SWyVwCRz2DpN8MEtQYSSlg8N7KboomvuhGMa7S12VeIQ1bKklrj4ge8hcKxjFm2zvdjGHRzRfOssxN1SqNmTj/Wx4SQQUEnwspBNa1dj9y69Qoj8W4pIxWNb9G6ddCH4Y6H7wUvJ1oubI0LQe3mFBRbQkgp7SJc849zMcQvB+t/BcSQ2yHjM73XlUKclNHEpCR9jpL5EM9IGWDm783MRtUqMu18xyL5FRDytEOpgfZkWNuqhkm/PnFBii1mZnpBooeRljvIzmkvIasH7UV8ZpRDRpci/Ng2HIOs1ZIMkCrQMac/kjLbQSoD/e3ztAgBAHzPOfhHYPd3Z16UHiFApMr4SJl3o+1BXuhm3O1jKdDyq+aBDr9IFohryBkW50ioD0VCiaG6nLha01DgIpr51axlU0ltTTCphJwMv91xa5tq64ioQAWy/5OidCzvLLM7JJLUsKExtd1TlQw4kexQrrit6wi3fjaeffe56GSkkcDfLI2FDkOEO4koAZrGvce+rX8rSOhSEtQkMEaWpjQ5q30DK348kdElTMsvR+359kNsQzsgKj68kIyqKDnQIUABbzeE/3FjZLQr6ac04Jweks5XUsvbSO3NLYbEJ9+WaKC2LyS69MGnkwp8560A4+IRNVXzRGUL3C3j0SXaz0DmZQIuv12I6sNsDVqySNFamua+pGmsCowRsl5Q/wjkhTZ+vTTJ0Xwy+br1PI14ZcF2MEEsdaJgoccTlyVHVVM2JCIY9Gi/50uW9KGFPcu697svQXm3Goe0EU9FwDvinI9Sqri0Wt5sJ11Rfg3BExyVLm3qIcZksox7ccfluEyiMv9G4rUIJOtwjYLMgdr5AKjdC7iS3GGCK1tTO0vBjTZz3C6Pi1ODcqcOGbkQcdvRY0pC9Ww/CkT6Mk4Pjrd6xohHJqE5+Okm4DBuP1XVcZke995XX3KLfks07I7dSojeFv9SlP4HEEdM8Q38Z9ziIgUiywlEKbqGQCZVqGZvPvHRqCA5n6MiyxKO3Xr8jeg56xzcgHF+bac0l66mE46GsfCjCXSagHLFYwSDJTgtiOVQL4xGRyRUz+vDZlCIOmuqLJs3tUsNSFPJXOiE9swlu+w5bNW+PfacPhgPxpdW3Ik94v0SzQyjFt/EBnCa6rYbcoUXjzKxdBV7/LtAm+L9+P4TYcIMr9C1w78MHnSA1isZ4FpvfUzpDzCkvZINt9og5bt+4k4wBWF+nvxX3LHk1TQfrFZmhGW4J9VW87JgjKOPJy41W8R2IIyTUNQNnw2IPflDCb+YMelsm7TD7UUYWPVjE3/B2Vc2gOFaGa/Qqvj2XTg0/2dmYQxIjj4P8Caa/Y7LJv9/MqLZgLOT4DcsfL5RRSUx7Ofw7AFmEbCommRVbKB1fqGV0r6B3FUTP2PtqVbEOtAqgEkxGDtdJcNC0Bc1awJJ/igVS7Ul4neSL8T5My4N4KPYWxgglf0ds9VWpL5C8XsqHp7u1m6b5eBpfNkyd4l2PWqTnvwsmhXm6oN8q8017ScdXhUC9e91exm+7aXwAT8ojqcMcEWBOd7i7S0c2VCdNRsPw9SBZlu3ntKaOOofOH2nJ/VvfBUNENcI5U83QHbBtxuQJPcEV7mouMOgYe20PMqfG7VFqgsP53uAeQffIHB2jg6KSd3VCeWjgtFU6RzvflWLiFsemaivZttBxM+po9xYj8WiWu0gbS4DTIbP0Quu3nlS9nxEh3JBQruULe6iekSoTfHM9UZ7AhbJku1qFgxlPpwl+8oFv38JrVRh1+OBqFfeoXA/DdlRicIqke5avAYnYveDd0Cs8wFjeGovu1S0AlJL3adEvpx+af+mt5U25GsiNjih82dsW53dvMxEUztH75Yyjd+iGwxKUDyvCDr+BP+wFgWehgSyXqgTHPpU2SkChKVtOiRsUTD1zzLp07tL1I8Rx/i9fSXA/vf5NlEdEuY4v2SzI+dHYuTwl0g0+q3IGMgNRB8u9/QsgN+0K3NmjTI9ovXrlOvmycn5JPP0SqJ7PFI/fJ4PxBerfUWWS4Jze1mOUO3N+ZbFHdIkLZzEWcU8/Xt/eQWGopomnsIZmXd/HqHXxxSXBL1N1CZWVUmmf+0zwqhZzvJ9zE8jjsL5icO23iJdquhPhmewMDbfyyskWcaAYQU54bBXtrCWuTfmiToNHkVpmW8Byq3O4dPb3EH/xLSAo9oVh3dhvZk8CMcbwdfOwvi+E/xbGG3OS4zcONYayFLbJxFp7B07As/M86fmjjwTw7GP19pNv59LpLOk0s/UO/Tcx9HCyaaLezqp2ZgflEvw41QB6n1Q7f3pjC1dqaQ51MzjvkIXvCrffYuGTcCe1ThZfEx+EHDclnN8RXC6ZIjXOmUMPjazFb1RUGDQO5wSHQi8DON/tr8Q5r03xMdEQljFLYkbEZY7Pw9yEjvKToua7Q23MZBNCJ/vus1oLOK/ImUuY5YpV8uyTMrEpC7/xgGmVabWb4P6SYmgmqLiqegSWAqlJGRzE/Wj+cuOwGtz7lhiUu5cvWcIppm4IsAapVJs25hnrWGuSy+c3otCa+/UcPG51jI3iwMJCojY+AXGbzhWDDS6gCN1tce9ZjrMEoT60b/LwaU5+E3jPlMcdrVeTIGXLLByC7m8dUK9h49m3eQE4EWkRuDrix5S7b6k0W/yrbaR3pQvR9MP31airpT34/74PGVhs79H4OC8AEcIT34Pl9putEYvxNlhntshzV1pxO9ytsDlq84v7+GSezLidAXUyyMZsEUSVGH9gVYnofRkV34g6l6VotZjKKUGxowwRhNyxkXSrMT13mhngLPkxGutm5ZkhJID8OfnjFzoQL8+u/qC0cVPw31mh6o/MKiOtiWovfa8qwBb1zFVEunvI3ME1w '; + + const samlp = new Samlp({ + decryptionKey: fs.readFileSync(__dirname + '/test-auth0.key') + }); + + samlp.extractAssertion(samlResponse, function (err, assertion) { + expect(err).not.to.exist; + expect(assertion).to.exist; + done(); + }); + }); + + it('should return error if more than one assertion is found', function (done) { + var currentSamlResponse = 'urn:fixture-testurn:fixture-test12345678https://auth0-dev-ed.my.salesforce.com12345678jfoo@gmail.comJohn FooJohnFoourn:oasis:names:tc:SAML:2.0:ac:classes:unspecifiedurn:fixture-test12345678https://auth0-dev-ed.my.salesforce.com12345678jfoo@gmail.comJohn FooJohnFoourn:oasis:names:tc:SAML:2.0:ac:classes:unspecifiedYkV3DdlEa19Gb0eE3jTYTVPalV1kZ88fbIv4blO9T1Y=ZiINpNlahQlp1JbgFsamI1/pZ+zcPsZboESVayxBMtrUBYNC4IG2VBnqku7paDxJQ7624CvcNzAYWYCv/2/c67Bv6YhQwK1rb4DPEL6OvbI8FNkYAhTNNw5UhUTEMjnJ7AncV/svUTYyIOyktuCvQh3tR4teZJV+BM3IKj9vRQQbCRNSUVHJEe963ma5HcCyo+RhIKU1pm4+ycswOlY9F115roKB4RNRJLs7Z5fyzhbOoCUujR9MMKHHq+CWaYvh5SkjaH1wMorlPlJtq5dhTZtDRhj4HwxYpCG5b4NF2vp+Jpni4dDFKou0Lzk0k6ueCJGcNHfidfEB3RB20Hed2g==MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo='; + var samlp = new Samlp({}); + samlp.extractAssertion(currentSamlResponse, function (err, assertion) { + expect(err).to.exist; + expect(assertion).not.to.exist; + expect(err).to.have.a.property('message', 'A SAMLResponse can contain only one Assertion element.'); + done(); + }); + }); + + }); + + describe('validateSamlResponse', function(){ + var samlpResponseWithStatusResponder = 'urn:fixture-test'; + var samlpResponseWithStatusResponderWithMessage = 'urn:fixture-testspecific error message'; + var samlpResponseWithStatusResponderAndAuthnFailed = 'urn:fixture-test'; + var samlpResponseWithStatusResponderAndAuthnFailedWithMessage = 'urn:fixture-testspecific error message'; + var samlpResponseWithStatusRequesterWithMessage = 'urn:fixture-testsignature required'; + var samlpResponseWithStatusRequesterWithoutMessage = 'urn:fixture-test'; + var samlpResponseWithStatusVersionMismatchWithMessage = 'urn:fixture-testversion mismatch error'; + var samlpResponseWithStatusVersionMismatchWithoutMessage = 'urn:fixture-test'; + var samlpResponseWithStatusNotMappedStatus = 'urn:fixture-test'; + var xmlWithNoSamlResponse = 'somedata'; + var xmlWithSeveralSamlResponseElements = 'urn:fixture-testurn:fixture-test'; + + it('should return error for AuthnFailed status with generic message', function(done){ + var samlp = new Samlp({ checkDestination: false }); + samlp.validateSamlResponse(samlpResponseWithStatusResponderAndAuthnFailed, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('AuthenticationFailedError'); + expect(err.message).to.equal('The responding provider was unable to successfully authenticate the principal'); + done(); + }); + }); + + it('should return error for AuthnFailed status with specific message', function(done){ + var samlp = new Samlp({ checkDestination: false }); + samlp.validateSamlResponse(samlpResponseWithStatusResponderAndAuthnFailedWithMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('AuthenticationFailedError'); + expect(err.message).to.equal('specific error message'); + done(); + }); + }); + + it('should return error for Responder status with generic message', function(done){ + var samlp = new Samlp({ checkDestination: false }); + samlp.validateSamlResponse(samlpResponseWithStatusResponder, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('AuthenticationFailedError'); + expect(err.message).to.equal('The request could not be performed due to an error on the part of the SAML responder or SAML authority'); + done(); + }); + }); + + it('should return error for Responder status with specific message', function(done){ + var samlp = new Samlp({ checkDestination: false }); + samlp.validateSamlResponse(samlpResponseWithStatusResponderWithMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('AuthenticationFailedError'); + expect(err.message).to.equal('specific error message'); + done(); + }); + }); + + it('should return error for Requester status with specific message', function(done){ + var samlp = new Samlp({ checkDestination: false }); + samlp.validateSamlResponse(samlpResponseWithStatusRequesterWithMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('AuthenticationFailedError'); + expect(err.message).to.equal('signature required'); + done(); + }); + }); + + it('should return error for Requester status with default message', function(done){ + var samlp = new Samlp({ checkDestination: false }); + samlp.validateSamlResponse(samlpResponseWithStatusRequesterWithoutMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('AuthenticationFailedError'); + expect(err.message).to.equal('The request could not be performed due to an error on the part of the requester'); + done(); + }); + }); + + it('should return error for VersionMismatch status with specific message', function(done){ + var samlp = new Samlp({ checkDestination: false }); + samlp.validateSamlResponse(samlpResponseWithStatusVersionMismatchWithMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('AuthenticationFailedError'); + expect(err.message).to.equal('version mismatch error'); + done(); + }); + }); + + it('should return error for VersionMismatch status with default message', function(done){ + var samlp = new Samlp({ checkDestination: false }); + samlp.validateSamlResponse(samlpResponseWithStatusVersionMismatchWithoutMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('AuthenticationFailedError'); + expect(err.message).to.equal('The SAML responder could not process the request because the version of the request message was incorrect.'); + done(); + }); + }); + + it('should return error when saml response is not found on the xml', function(done){ + var samlp = new Samlp({}); + samlp.validateSamlResponse(xmlWithNoSamlResponse, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('Error'); + expect(err.message).to.equal('XML is not a valid saml response'); + done(); + }); + }); + + it('should return error when saml response is found more than once', function(done){ + var samlp = new Samlp({}); + samlp.validateSamlResponse(xmlWithSeveralSamlResponseElements, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('Error'); + expect(err.message).to.equal('SAMLResponse should be unique'); + done(); + }); + }); + + it('should return \'saml response does not contain an Assertion element\' error', function(done){ + var samlp = new Samlp({ checkDestination: false }); + samlp.validateSamlResponse(samlpResponseWithStatusNotMappedStatus, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('Error'); + expect(err.message).to.equal('saml response does not contain an Assertion element (Status: urn:oasis:names:tc:SAML:2.0:status:Success)'); + done(); + }); + }); + + it('should return error for Destination does not match', function(done){ + var samlp = new Samlp({ destinationUrl: 'invalid' }); + samlp.validateSamlResponse(samlpResponseWithStatusResponderWithMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('Error'); + expect(err.message).to.equal('Destination endpoint https://auth0-dev-ed.my.salesforce.com did not match invalid'); + done(); + }); + }); + + it('should return error for if isValidResponseID fails', function(done){ + var samlp = new Samlp({ destinationUrl: 'invalid', isValidResponseID: function(samlResponseID, done) { + return done(new Error('Invalid response id')) + } }); + samlp.validateSamlResponse(samlpResponseWithStatusResponderWithMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('Error'); + expect(err.message).to.equal('Invalid response id'); + done(); + }); + }); + + it('should not return ResponseID validation error for if isValidResponseID fails but the check is disabled', function(done){ + var isValidResponseID = function(samlResponseID, done) { + return done(new Error('Invalid response id')) + } + var samlp = new Samlp({ destinationUrl: 'invalid', checkResponseID: false, isValidResponseID: isValidResponseID }); + samlp.validateSamlResponse(samlpResponseWithStatusResponderWithMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('Error'); + expect(err.message).not.to.equal('Invalid response id'); + expect(err.message).to.equal('Destination endpoint https://auth0-dev-ed.my.salesforce.com did not match invalid'); + done(); + }); + }); + + it('should return error for if isValidInResponseTo fails', function(done){ + var samlp = new Samlp({ destinationUrl: 'invalid', isValidInResponseTo: function(inReponseTo, done) { + return done(new Error('Invalid inResponseTo')) + } }); + + samlp.validateSamlResponse(samlpResponseWithStatusResponderWithMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('Error'); + expect(err.message).to.equal('Invalid inResponseTo'); + done(); + }); + }); + + it('should not return InResponseTo validation error for if isValidInResponseTo fails but the check is disabled', function(done){ + var isValidInResponseTo = function(inReponseTo, done) { + return done(new Error('Invalid inResponseTo')) + } + var samlp = new Samlp({ destinationUrl: 'invalid', checkInResponseTo: false, isValidInResponseTo: isValidInResponseTo }); + samlp.validateSamlResponse(samlpResponseWithStatusResponderWithMessage, function (err) { + expect(err).to.be.ok; + expect(err.name).to.equals('Error'); //Destination is invalid + expect(err.message).not.to.equal('Invalid inResponseTo'); + expect(err.message).to.equal('Destination endpoint https://auth0-dev-ed.my.salesforce.com did not match invalid'); + done(); + }); + }); + + it('should return profile even if the namespace is in response element', function(done){ + var cert = fs.readFileSync(__dirname + '/test-auth0.cer'); + var encodedSamlResponse = 'PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIgeG1sbnM6ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOng1MDA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm9maWxlczphdHRyaWJ1dGU6WDUwMCIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgRGVzdGluYXRpb249Imh0dHBzOi8vYXZpbGxhY2hsYWIuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249Q0hPUCIgSUQ9InBmeDJiYTM1MDM4LTdmZmYtZjljMC1jOWJjLTE0NjJlMTQ1NWE3NiIgSXNzdWVJbnN0YW50PSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHA6Ly9jaWRtZmVkLmNob3AuZWR1L29hbS9mZWQ8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmU+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDJiYTM1MDM4LTdmZmYtZjljMC1jOWJjLTE0NjJlMTQ1NWE3NiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+d0ZLLy9YN0dBdzVQQlFIbnRQV2I4T1RoWkVFPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT50SWI4WjZPV3ExVDBzd3M2SkZkQWJVUjZGRUJrM0k3TmtYZ2s1d0N0NDJ0TWpQcTM0M2o4YWoxeHdKcXNiWXZMVHZBdHhFZ21vaGd4dmNKN29BRGlxWEJnRFE2SEpOeGUzVTZxM05HTzZRN1hobXRITUZOK2JmK0JsVDdIbGw2TWExMUJmWU5pNnJLblJPcUpUTDZlem01M2pMTm5xazlFbi9HWXdjQUttR0kxQzF4bEo5Y1FEdUh6QTZ3NTdUZXhkQU9YbkJWTWk1MG9Bb0FHOHRhVURXdHBwUXdmdXVDRitEN056NVFvVU5VS0UvRXh0VGpyaUJnMDRSWHY2Z0ZUS3FZYmViNHFETUlxZjZoZ3BWZDF4cm9aaXBHZlFodUhvY2pvVUtRU2ZTUDhCRFlEVFpveFZJaUVCVUhQOFJSSzVYb2Y0NXgwK2ZZajErTzdrZzhWcEE9PTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUVEekNDQXZlZ0F3SUJBZ0lKQUxyOUh3Z3JRN0dlTUEwR0NTcUdTSWIzRFFFQkJRVUFNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaREFlRncweE1qRXlNamt4TlRNd05EZGFGdzB4TXpBeE1qZ3hOVE13TkRkYU1HSXhHREFXQmdOVkJBTVREMkYxZEdnd0xtRjFkR2d3TG1OdmJURVNNQkFHQTFVRUNoTUpRWFYwYURBZ1RFeERNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1YyRnphR2x1WjNSdmJqRVFNQTRHQTFVRUJ4TUhVbVZrYlc5dVpEQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1aaVZtTkhpWExsZHJnYlM1ME9OTk9IN3BKMnpnNk9jU01rWVpHRFpKYk9aL1Rxd2F1QzZKT25JNyt4dGtQSnNRSFpTRkpzNFUwc3JqWkt6RENtYXoyakxBSkRTaFAyamFYbHJraTE2bkRMUEUvL0lHQWczQkpndVNtQkNXcERiU205MlY5aFNzRStNaHg2YkRhSml3OHlRK1E4aVNtMGFUUVp0cDZPNElDTXUwMEVTZGg5TkpxSUVDRUx2UDMxQURWMVhoajdJYnl5VlBERnhNdjNvbDVCeVNFOXd3d09GVXEvd3Y3WHo5TFJpVWpVelBPK0xxM09NM28vdUNEYms3akQ3WHJHVXVPeWRBTEQ4VUxzWHA0RXVETytuRmJlWEIvaUtuZFp5bnVWS29raXJ5d2wybkQySVAwL3luY2RMUVo4QnlJeXFQM0c4MmZxL2w4cDdBc0NBd0VBQWFPQnh6Q0J4REFkQmdOVkhRNEVGZ1FVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBZd2daUUdBMVVkSXdTQmpEQ0JpWUFVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBhaFpxUmtNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaSUlKQUxyOUh3Z3JRN0dlTUF3R0ExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRnJYSWhDeTRUNGVHcmlrYjBSMndIdi91UzU0OHIzcFp5QlYwQ0RiY1J3QXRibnBKTXZrR0ZxS1ZwNHBteW9JRFNWTksvaitzTEVzaEIyMFhmdGV6SFp5UkpiQ1VidEt2WFE2RnN4b2VaTWxOMElUWUtUYW9CWktoVXh4ajkwb3RBaE5DNThxd0dVUHF0Mkxld0poSHlMdWNLa0dKMW1RM2I1eEtaNTMyVG91Zm91SDlWTGhpZzNIMUtueFdvL3pNRDZLZThjQ2s2cU85aHR1aEkwNnMzR1FHUzFRV1F0QW1tMTdDNlRmS2dEd1FGWndocUhVVVpud0tSSDhnVTZPZ1pzdmhnVjFCN0g1bWpaY3U1N0tNaURCZWtVOU1FWTBEQ1ZUTjNXa21jVElJNjY4ekxzSnJrTlg2UEVmY2sxQU1CYlZFNnBFVUtjV3dxM3VhTHZsQVVvPTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiBJRD0iaWQtWS1Sd0hpNlJQOGpNVVI4a3IxRlZ6SHVOdmJ1ck9JZUs2d0dwTmpkLSIgSXNzdWVJbnN0YW50PSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHA6Ly9jaWRtZmVkLmNob3AuZWR1L29hbS9mZWQ8L3NhbWw6SXNzdWVyPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6dW5zcGVjaWZpZWQiPkhhbmtlZUpAZW1haWwuY2hvcC5lZHU8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTYtMDgtMTBUMTk6MjU6MjhaIiBSZWNpcGllbnQ9Imh0dHBzOi8vYXZpbGxhY2hsYWIuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249Q0hPUCIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgTm90T25PckFmdGVyPSIyMDE2LTA4LTEwVDE5OjI1OjI4WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT51cm46YXV0aDA6YXZpbGxhY2hsYWI6Q0hPUDwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTYtMDgtMTBUMTk6MjA6MjhaIiBTZXNzaW9uSW5kZXg9ImlkLXZNVy0zckstdlJlb2V1T2Q1QXRWOEpiLVFRNENtUTB6RzQ1ZlRZSjEiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTYtMDgtMTBUMjA6MjA6MjhaIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+TERBUFNjaGVtZV9HUklOPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4='; + const samlResponse = new Buffer(encodedSamlResponse, 'base64').toString(); + var options = { + cert: cert, + thumbprint: '5CA6E1202EAFC0A63A5B93A43572EB2376FED309', + checkExpiration: false, + checkDestination: false, + checkRecipient: false, + realm: 'urn:auth0:avillachlab:CHOP' + }; + var samlp = new Samlp(options, new Saml(options)); + samlp.validateSamlResponse(samlResponse, function (err, profile) { + if (err) return done(err); + expect(profile).to.be.ok; + done(); + }); + }); + + it('should return profile even if the namespace is in response element and assertion is signed', function(done){ + var cert = fs.readFileSync(__dirname + '/test-auth0.cer'); + var encodedSamlResponse = 'PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIgeG1sbnM6ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOng1MDA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm9maWxlczphdHRyaWJ1dGU6WDUwMCIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgRGVzdGluYXRpb249Imh0dHBzOi8vYXZpbGxhY2hsYWIuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249Q0hPUCIgSUQ9InBmeDBiZDdlODQyLTZiZjUtNjE4YS1jOTEwLTJlOTUwNGVlZDgyZiIgSXNzdWVJbnN0YW50PSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHA6Ly9jaWRtZmVkLmNob3AuZWR1L29hbS9mZWQ8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmU+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDBiZDdlODQyLTZiZjUtNjE4YS1jOTEwLTJlOTUwNGVlZDgyZiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+cmJPZkR2dkxTVXFmdWpZY1cxYjBMOGFsd2YwPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5NWUhzS0p2eXZrRGVBOHc0ODVQVjRRYlFzeklRb1RlV2IrTGRSa2s5eG9mVmdGMzI1d1BuQk03ckYrTWVaOWZ0MTNuaHVXM0pwbWhLTEpuV2VRenpwREN4SmU4eVcxRHlFL2tIeitGRU1PdDRkNGdLQVVCdVM1ZHloMzA3ZGhPRlluRE9DeDlyL29SbkZDenN1Rlh1STR4UjhEalJWdzl3LzhJQ0NSQ0Z6T0svTFpzZ3BTd215bTFDcm1tK25YcFB1T3prU0psMU1VczlVZEdBeW8wWTBNeVhMS3lidnZaYlR5S0FJZXpRRlNkcjJ3ejRoMXk5SU9KdnBHcmd2M0J1N3pONnRqSUpRTG1FZFZrN3VnWWFRMXJvOWpEMEZqazNOZ0VSRm5EZEVBbW84Y2FsSVM5VlczcFcyZzIwMzIyRGF5a3k2ZmV1bXBKWXpkNFpyQXZvVkE9PTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUVEekNDQXZlZ0F3SUJBZ0lKQUxyOUh3Z3JRN0dlTUEwR0NTcUdTSWIzRFFFQkJRVUFNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaREFlRncweE1qRXlNamt4TlRNd05EZGFGdzB4TXpBeE1qZ3hOVE13TkRkYU1HSXhHREFXQmdOVkJBTVREMkYxZEdnd0xtRjFkR2d3TG1OdmJURVNNQkFHQTFVRUNoTUpRWFYwYURBZ1RFeERNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1YyRnphR2x1WjNSdmJqRVFNQTRHQTFVRUJ4TUhVbVZrYlc5dVpEQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1aaVZtTkhpWExsZHJnYlM1ME9OTk9IN3BKMnpnNk9jU01rWVpHRFpKYk9aL1Rxd2F1QzZKT25JNyt4dGtQSnNRSFpTRkpzNFUwc3JqWkt6RENtYXoyakxBSkRTaFAyamFYbHJraTE2bkRMUEUvL0lHQWczQkpndVNtQkNXcERiU205MlY5aFNzRStNaHg2YkRhSml3OHlRK1E4aVNtMGFUUVp0cDZPNElDTXUwMEVTZGg5TkpxSUVDRUx2UDMxQURWMVhoajdJYnl5VlBERnhNdjNvbDVCeVNFOXd3d09GVXEvd3Y3WHo5TFJpVWpVelBPK0xxM09NM28vdUNEYms3akQ3WHJHVXVPeWRBTEQ4VUxzWHA0RXVETytuRmJlWEIvaUtuZFp5bnVWS29raXJ5d2wybkQySVAwL3luY2RMUVo4QnlJeXFQM0c4MmZxL2w4cDdBc0NBd0VBQWFPQnh6Q0J4REFkQmdOVkhRNEVGZ1FVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBZd2daUUdBMVVkSXdTQmpEQ0JpWUFVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBhaFpxUmtNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaSUlKQUxyOUh3Z3JRN0dlTUF3R0ExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRnJYSWhDeTRUNGVHcmlrYjBSMndIdi91UzU0OHIzcFp5QlYwQ0RiY1J3QXRibnBKTXZrR0ZxS1ZwNHBteW9JRFNWTksvaitzTEVzaEIyMFhmdGV6SFp5UkpiQ1VidEt2WFE2RnN4b2VaTWxOMElUWUtUYW9CWktoVXh4ajkwb3RBaE5DNThxd0dVUHF0Mkxld0poSHlMdWNLa0dKMW1RM2I1eEtaNTMyVG91Zm91SDlWTGhpZzNIMUtueFdvL3pNRDZLZThjQ2s2cU85aHR1aEkwNnMzR1FHUzFRV1F0QW1tMTdDNlRmS2dEd1FGWndocUhVVVpud0tSSDhnVTZPZ1pzdmhnVjFCN0g1bWpaY3U1N0tNaURCZWtVOU1FWTBEQ1ZUTjNXa21jVElJNjY4ekxzSnJrTlg2UEVmY2sxQU1CYlZFNnBFVUtjV3dxM3VhTHZsQVVvPTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiBJRD0icGZ4ZDYzODRjOGUtYmYwYi1kODE5LTlmZDItMjE2M2M1MTJlZjY0IiBJc3N1ZUluc3RhbnQ9IjIwMTYtMDgtMTBUMTk6MjA6MjhaIiBWZXJzaW9uPSIyLjAiPjxzYW1sOklzc3VlciBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSI+aHR0cDovL2NpZG1mZWQuY2hvcC5lZHUvb2FtL2ZlZDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZT4KICA8ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPgogIDxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4ZDYzODRjOGUtYmYwYi1kODE5LTlmZDItMjE2M2M1MTJlZjY0Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT56SEhGRkI0SkhWallFSnlKWFZrN0M0UUFuTDg9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPk85aS9pb0c5TUNjMUwxM2hqMkoxb3VsaURVK29FOFRFMk9DYWdHanJuM2JaZHBTVDJQM2JKdGFBMXZTWm9sc28xZVRqbjJneWFQM1ZhMno4Q2VScWZoZCtmbHVzS1FKZXRWT0JoZGFMRXU1QnZ3Nm51ZldoTG9sZk5uMVBtR2RFRGRDVU1pWTlOQzFud0laOHN6dkdMNTRDYTl4dmpzbytvY1kvS0drNGpYSHlnSnkyN0lvTFNqMThZSzN2WFBKbUM5N1h6S1VteUxPTUlCaTl3ZitoU1pSa1dUQjVlakRGVWZuekxQL3ZCaHFSVVBZeGFmdjFZU050amJSUE8zSXlub2RzS3F0cVdndmN1ekNHcVAvdFpLWjE4NW14dGxvMnFQUkkxMVk0eDNNZzBidjBIQUJuSXdGcVA0N2EyWFllZU1ZNzFjL0VyNzY2eGpQeklGMFFOQT09PC9kczpTaWduYXR1cmVWYWx1ZT4KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRUR6Q0NBdmVnQXdJQkFnSUpBTHI5SHdnclE3R2VNQTBHQ1NxR1NJYjNEUUVCQlFVQU1HSXhHREFXQmdOVkJBTVREMkYxZEdnd0xtRjFkR2d3TG1OdmJURVNNQkFHQTFVRUNoTUpRWFYwYURBZ1RFeERNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1YyRnphR2x1WjNSdmJqRVFNQTRHQTFVRUJ4TUhVbVZrYlc5dVpEQWVGdzB4TWpFeU1qa3hOVE13TkRkYUZ3MHhNekF4TWpneE5UTXdORGRhTUdJeEdEQVdCZ05WQkFNVEQyRjFkR2d3TG1GMWRHZ3dMbU52YlRFU01CQUdBMVVFQ2hNSlFYVjBhREFnVEV4RE1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQk1LVjJGemFHbHVaM1J2YmpFUU1BNEdBMVVFQnhNSFVtVmtiVzl1WkRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTVppVm1OSGlYTGxkcmdiUzUwT05OT0g3cEoyemc2T2NTTWtZWkdEWkpiT1ovVHF3YXVDNkpPbkk3K3h0a1BKc1FIWlNGSnM0VTBzcmpaS3pEQ21hejJqTEFKRFNoUDJqYVhscmtpMTZuRExQRS8vSUdBZzNCSmd1U21CQ1dwRGJTbTkyVjloU3NFK01oeDZiRGFKaXc4eVErUThpU20wYVRRWnRwNk80SUNNdTAwRVNkaDlOSnFJRUNFTHZQMzFBRFYxWGhqN0lieXlWUERGeE12M29sNUJ5U0U5d3d3T0ZVcS93djdYejlMUmlValV6UE8rTHEzT00zby91Q0RiazdqRDdYckdVdU95ZEFMRDhVTHNYcDRFdURPK25GYmVYQi9pS25kWnludVZLb2tpcnl3bDJuRDJJUDAveW5jZExRWjhCeUl5cVAzRzgyZnEvbDhwN0FzQ0F3RUFBYU9CeHpDQnhEQWRCZ05WSFE0RUZnUVVISTJyVVhlQmpUdjF6QWxsYVBHckhGY0VLMFl3Z1pRR0ExVWRJd1NCakRDQmlZQVVISTJyVVhlQmpUdjF6QWxsYVBHckhGY0VLMGFoWnFSa01HSXhHREFXQmdOVkJBTVREMkYxZEdnd0xtRjFkR2d3TG1OdmJURVNNQkFHQTFVRUNoTUpRWFYwYURBZ1RFeERNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1YyRnphR2x1WjNSdmJqRVFNQTRHQTFVRUJ4TUhVbVZrYlc5dVpJSUpBTHI5SHdnclE3R2VNQXdHQTFVZEV3UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUZCUUFEZ2dFQkFGclhJaEN5NFQ0ZUdyaWtiMFIyd0h2L3VTNTQ4cjNwWnlCVjBDRGJjUndBdGJucEpNdmtHRnFLVnA0cG15b0lEU1ZOSy9qK3NMRXNoQjIwWGZ0ZXpIWnlSSmJDVWJ0S3ZYUTZGc3hvZVpNbE4wSVRZS1Rhb0JaS2hVeHhqOTBvdEFoTkM1OHF3R1VQcXQyTGV3SmhIeUx1Y0trR0oxbVEzYjV4S1o1MzJUb3Vmb3VIOVZMaGlnM0gxS254V28vek1ENktlOGNDazZxTzlodHVoSTA2czNHUUdTMVFXUXRBbW0xN0M2VGZLZ0R3UUZad2hxSFVVWm53S1JIOGdVNk9nWnN2aGdWMUI3SDVtalpjdTU3S01pREJla1U5TUVZMERDVlROM1drbWNUSUk2Njh6THNKcmtOWDZQRWZjazFBTUJiVkU2cEVVS2NXd3EzdWFMdmxBVW89PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDp1bnNwZWNpZmllZCI+SGFua2VlSkBlbWFpbC5jaG9wLmVkdTwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxNi0wOC0xMFQxOToyNToyOFoiIFJlY2lwaWVudD0iaHR0cHM6Ly9hdmlsbGFjaGxhYi5hdXRoMC5jb20vbG9naW4vY2FsbGJhY2s/Y29ubmVjdGlvbj1DSE9QIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTYtMDgtMTBUMTk6MjA6MjhaIiBOb3RPbk9yQWZ0ZXI9IjIwMTYtMDgtMTBUMTk6MjU6MjhaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPnVybjphdXRoMDphdmlsbGFjaGxhYjpDSE9QPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNi0wOC0xMFQxOToyMDoyOFoiIFNlc3Npb25JbmRleD0iaWQtdk1XLTNySy12UmVvZXVPZDVBdFY4SmItUVE0Q21RMHpHNDVmVFlKMSIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxNi0wOC0xMFQyMDoyMDoyOFoiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj5MREFQU2NoZW1lX0dSSU48L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg=='; + const samlResponse = new Buffer(encodedSamlResponse, 'base64').toString(); + var options = { + cert: cert, + thumbprint: '5CA6E1202EAFC0A63A5B93A43572EB2376FED309', + checkExpiration: false, + checkDestination: false, + checkRecipient: false, + realm: 'urn:auth0:avillachlab:CHOP' + }; + var samlp = new Samlp(options, new Saml(options)); + samlp.validateSamlResponse(samlResponse, function (err, profile) { + if (err) return done(err); + expect(profile).to.be.ok; + done(); + }); + }); + + it('should return profile even if the namespace is in response element', function(done){ + var cert = fs.readFileSync(__dirname + '/test-auth0.cer'); + var encodedSamlResponse = 'PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIgeG1sbnM6ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOng1MDA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm9maWxlczphdHRyaWJ1dGU6WDUwMCIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgRGVzdGluYXRpb249Imh0dHBzOi8vYXZpbGxhY2hsYWIuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249Q0hPUCIgSUQ9InBmeDJiYTM1MDM4LTdmZmYtZjljMC1jOWJjLTE0NjJlMTQ1NWE3NiIgSXNzdWVJbnN0YW50PSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHA6Ly9jaWRtZmVkLmNob3AuZWR1L29hbS9mZWQ8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmU+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDJiYTM1MDM4LTdmZmYtZjljMC1jOWJjLTE0NjJlMTQ1NWE3NiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+d0ZLLy9YN0dBdzVQQlFIbnRQV2I4T1RoWkVFPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT50SWI4WjZPV3ExVDBzd3M2SkZkQWJVUjZGRUJrM0k3TmtYZ2s1d0N0NDJ0TWpQcTM0M2o4YWoxeHdKcXNiWXZMVHZBdHhFZ21vaGd4dmNKN29BRGlxWEJnRFE2SEpOeGUzVTZxM05HTzZRN1hobXRITUZOK2JmK0JsVDdIbGw2TWExMUJmWU5pNnJLblJPcUpUTDZlem01M2pMTm5xazlFbi9HWXdjQUttR0kxQzF4bEo5Y1FEdUh6QTZ3NTdUZXhkQU9YbkJWTWk1MG9Bb0FHOHRhVURXdHBwUXdmdXVDRitEN056NVFvVU5VS0UvRXh0VGpyaUJnMDRSWHY2Z0ZUS3FZYmViNHFETUlxZjZoZ3BWZDF4cm9aaXBHZlFodUhvY2pvVUtRU2ZTUDhCRFlEVFpveFZJaUVCVUhQOFJSSzVYb2Y0NXgwK2ZZajErTzdrZzhWcEE9PTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUVEekNDQXZlZ0F3SUJBZ0lKQUxyOUh3Z3JRN0dlTUEwR0NTcUdTSWIzRFFFQkJRVUFNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaREFlRncweE1qRXlNamt4TlRNd05EZGFGdzB4TXpBeE1qZ3hOVE13TkRkYU1HSXhHREFXQmdOVkJBTVREMkYxZEdnd0xtRjFkR2d3TG1OdmJURVNNQkFHQTFVRUNoTUpRWFYwYURBZ1RFeERNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1YyRnphR2x1WjNSdmJqRVFNQTRHQTFVRUJ4TUhVbVZrYlc5dVpEQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1aaVZtTkhpWExsZHJnYlM1ME9OTk9IN3BKMnpnNk9jU01rWVpHRFpKYk9aL1Rxd2F1QzZKT25JNyt4dGtQSnNRSFpTRkpzNFUwc3JqWkt6RENtYXoyakxBSkRTaFAyamFYbHJraTE2bkRMUEUvL0lHQWczQkpndVNtQkNXcERiU205MlY5aFNzRStNaHg2YkRhSml3OHlRK1E4aVNtMGFUUVp0cDZPNElDTXUwMEVTZGg5TkpxSUVDRUx2UDMxQURWMVhoajdJYnl5VlBERnhNdjNvbDVCeVNFOXd3d09GVXEvd3Y3WHo5TFJpVWpVelBPK0xxM09NM28vdUNEYms3akQ3WHJHVXVPeWRBTEQ4VUxzWHA0RXVETytuRmJlWEIvaUtuZFp5bnVWS29raXJ5d2wybkQySVAwL3luY2RMUVo4QnlJeXFQM0c4MmZxL2w4cDdBc0NBd0VBQWFPQnh6Q0J4REFkQmdOVkhRNEVGZ1FVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBZd2daUUdBMVVkSXdTQmpEQ0JpWUFVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBhaFpxUmtNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaSUlKQUxyOUh3Z3JRN0dlTUF3R0ExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRnJYSWhDeTRUNGVHcmlrYjBSMndIdi91UzU0OHIzcFp5QlYwQ0RiY1J3QXRibnBKTXZrR0ZxS1ZwNHBteW9JRFNWTksvaitzTEVzaEIyMFhmdGV6SFp5UkpiQ1VidEt2WFE2RnN4b2VaTWxOMElUWUtUYW9CWktoVXh4ajkwb3RBaE5DNThxd0dVUHF0Mkxld0poSHlMdWNLa0dKMW1RM2I1eEtaNTMyVG91Zm91SDlWTGhpZzNIMUtueFdvL3pNRDZLZThjQ2s2cU85aHR1aEkwNnMzR1FHUzFRV1F0QW1tMTdDNlRmS2dEd1FGWndocUhVVVpud0tSSDhnVTZPZ1pzdmhnVjFCN0g1bWpaY3U1N0tNaURCZWtVOU1FWTBEQ1ZUTjNXa21jVElJNjY4ekxzSnJrTlg2UEVmY2sxQU1CYlZFNnBFVUtjV3dxM3VhTHZsQVVvPTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiBJRD0iaWQtWS1Sd0hpNlJQOGpNVVI4a3IxRlZ6SHVOdmJ1ck9JZUs2d0dwTmpkLSIgSXNzdWVJbnN0YW50PSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHA6Ly9jaWRtZmVkLmNob3AuZWR1L29hbS9mZWQ8L3NhbWw6SXNzdWVyPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6dW5zcGVjaWZpZWQiPkhhbmtlZUpAZW1haWwuY2hvcC5lZHU8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTYtMDgtMTBUMTk6MjU6MjhaIiBSZWNpcGllbnQ9Imh0dHBzOi8vYXZpbGxhY2hsYWIuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249Q0hPUCIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgTm90T25PckFmdGVyPSIyMDE2LTA4LTEwVDE5OjI1OjI4WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT51cm46YXV0aDA6YXZpbGxhY2hsYWI6Q0hPUDwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTYtMDgtMTBUMTk6MjA6MjhaIiBTZXNzaW9uSW5kZXg9ImlkLXZNVy0zckstdlJlb2V1T2Q1QXRWOEpiLVFRNENtUTB6RzQ1ZlRZSjEiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTYtMDgtMTBUMjA6MjA6MjhaIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+TERBUFNjaGVtZV9HUklOPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4='; + const samlResponse = new Buffer(encodedSamlResponse, 'base64').toString(); + var options = { + cert: cert, + thumbprint: '5CA6E1202EAFC0A63A5B93A43572EB2376FED309', + checkExpiration: false, + checkDestination: false, + checkRecipient: false, + realm: 'urn:auth0:avillachlab:CHOP' + }; + var samlp = new Samlp(options, new Saml(options)); + samlp.validateSamlResponse(samlResponse, function (err, profile) { + if (err) return done(err); + expect(profile).to.be.ok; + done(); + }); + }); + + it('should return profile when attribute namespaces are defined in saml response', function(done){ + var encodedSamlResponse = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiB4bWxuczplbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6eDUwMD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb2ZpbGVzOmF0dHJpYnV0ZTpYNTAwIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9maXJlZ2xhc3MuZXUuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249cHV0bmFtIiBJRD0iaWQtVERVNUw3WnVVU0p0ZWFMZzNXbzZVTEgtN1BId3JqWlZvQzlJQ29haCIgSW5SZXNwb25zZVRvPSJfYTBmNTgwZGYwNGMyZWIwMjE3MzUiIElzc3VlSW5zdGFudD0iMjAxNi0wOC0yOVQxOTozMzoyMloiIFZlcnNpb249IjIuMCI+CiAgPHNhbWw6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwczovL29hbS1zdGcucHV0bmFtLmNvbS9vYW0vZmVkPC9zYW1sOklzc3Vlcj4KICA8c2FtbHA6U3RhdHVzPgogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPgogIDxzYW1sOkFzc2VydGlvbiBJRD0icGZ4OTlmNmNlMWMtMWE0Ni03Yzk3LTU5MTYtMzRkYTFlZmQ3NGIzIiBJc3N1ZUluc3RhbnQ9IjIwMTYtMDgtMjlUMTk6MzM6MjJaIiBWZXJzaW9uPSIyLjAiPgogICAgPHNhbWw6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwczovL29hbS1zdGcucHV0bmFtLmNvbS9vYW0vZmVkPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlPgogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNwZng5OWY2Y2UxYy0xYTQ2LTdjOTctNTkxNi0zNGRhMWVmZDc0YjMiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPlhmNmEzWTB4d2paZjkyMW5QMjBvT1ZaY09ZUT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+THRxcW5YRUVpRUpvejNDVEJiS0I0M1RZbytudVNacW9iY2Z1bTNhOW0vaHJyVSs2VHRJdWJsblRYQkhsLzU1Y3kwc2pBa2dDL2M3MWpTbU0wQ0owVWNwNjNNdkxoeERnUUdpazBERXNyQnE4UmxHaENDeG9lM0o0elk0OXdmY3ZtUVdXOHlyMG44aG5WcWtNNWV0K3VSTjV2YTNaSjNZdkcwK0NiNEtjNE1CQmgxWDZKUGZhWHQvcFZTQzVTU21VM1FrakpCbUowN2ZobHRJTHJsZVFvYUxmZy84SDFid054M1dETysxd3J3NHo0MEYyTFdnL1huc21ZSzBNZkJKNVFrcHFISUpqU29kbWI5Qy9lS1BCNmRXNE82ZndIS3JaMkFSN2Y5QlhORzN3MnNRbVRzWDFzd0pnd2V3MGpDbzUycjhtV2FHbzlDb3RVN1dZUkwwQXRBPT08L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRHpDQ0F2ZWdBd0lCQWdJSkFMcjlId2dyUTdHZU1BMEdDU3FHU0liM0RRRUJCUVVBTUdJeEdEQVdCZ05WQkFNVEQyRjFkR2d3TG1GMWRHZ3dMbU52YlRFU01CQUdBMVVFQ2hNSlFYVjBhREFnVEV4RE1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQk1LVjJGemFHbHVaM1J2YmpFUU1BNEdBMVVFQnhNSFVtVmtiVzl1WkRBZUZ3MHhNakV5TWpreE5UTXdORGRhRncweE16QXhNamd4TlRNd05EZGFNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNWmlWbU5IaVhMbGRyZ2JTNTBPTk5PSDdwSjJ6ZzZPY1NNa1laR0RaSmJPWi9UcXdhdUM2Sk9uSTcreHRrUEpzUUhaU0ZKczRVMHNyalpLekRDbWF6MmpMQUpEU2hQMmphWGxya2kxNm5ETFBFLy9JR0FnM0JKZ3VTbUJDV3BEYlNtOTJWOWhTc0UrTWh4NmJEYUppdzh5UStROGlTbTBhVFFadHA2TzRJQ011MDBFU2RoOU5KcUlFQ0VMdlAzMUFEVjFYaGo3SWJ5eVZQREZ4TXYzb2w1QnlTRTl3d3dPRlVxL3d2N1h6OUxSaVVqVXpQTytMcTNPTTNvL3VDRGJrN2pEN1hyR1V1T3lkQUxEOFVMc1hwNEV1RE8rbkZiZVhCL2lLbmRaeW51Vktva2lyeXdsMm5EMklQMC95bmNkTFFaOEJ5SXlxUDNHODJmcS9sOHA3QXNDQXdFQUFhT0J4ekNCeERBZEJnTlZIUTRFRmdRVUhJMnJVWGVCalR2MXpBbGxhUEdySEZjRUswWXdnWlFHQTFVZEl3U0JqRENCaVlBVUhJMnJVWGVCalR2MXpBbGxhUEdySEZjRUswYWhacVJrTUdJeEdEQVdCZ05WQkFNVEQyRjFkR2d3TG1GMWRHZ3dMbU52YlRFU01CQUdBMVVFQ2hNSlFYVjBhREFnVEV4RE1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQk1LVjJGemFHbHVaM1J2YmpFUU1BNEdBMVVFQnhNSFVtVmtiVzl1WklJSkFMcjlId2dyUTdHZU1Bd0dBMVVkRXdRRk1BTUJBZjh3RFFZSktvWklodmNOQVFFRkJRQURnZ0VCQUZyWEloQ3k0VDRlR3Jpa2IwUjJ3SHYvdVM1NDhyM3BaeUJWMENEYmNSd0F0Ym5wSk12a0dGcUtWcDRwbXlvSURTVk5LL2orc0xFc2hCMjBYZnRlekhaeVJKYkNVYnRLdlhRNkZzeG9lWk1sTjBJVFlLVGFvQlpLaFV4eGo5MG90QWhOQzU4cXdHVVBxdDJMZXdKaEh5THVjS2tHSjFtUTNiNXhLWjUzMlRvdWZvdUg5VkxoaWczSDFLbnhXby96TUQ2S2U4Y0NrNnFPOWh0dWhJMDZzM0dRR1MxUVdRdEFtbTE3QzZUZktnRHdRRlp3aHFIVVVabndLUkg4Z1U2T2dac3ZoZ1YxQjdINW1qWmN1NTdLTWlEQmVrVTlNRVkwRENWVE4zV2ttY1RJSTY2OHpMc0pya05YNlBFZmNrMUFNQmJWRTZwRVVLY1d3cTN1YUx2bEFVbz08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT4KICAgIDxzYW1sOlN1YmplY3Q+CiAgICAgIDxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OnVuc3BlY2lmaWVkIj5EZW1vX1VzZXJAcHV0bmFtLmNvbTwvc2FtbDpOYW1lSUQ+CiAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4KICAgICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBJblJlc3BvbnNlVG89Il9hMGY1ODBkZjA0YzJlYjAyMTczNSIgTm90T25PckFmdGVyPSIyMDE2LTA4LTI5VDE5OjM4OjIyWiIgUmVjaXBpZW50PSJodHRwczovL2ZpcmVnbGFzcy5ldS5hdXRoMC5jb20vbG9naW4vY2FsbGJhY2s/Y29ubmVjdGlvbj1wdXRuYW0iLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4KICAgIDwvc2FtbDpTdWJqZWN0PgogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTYtMDgtMjlUMTk6MzM6MjJaIiBOb3RPbk9yQWZ0ZXI9IjIwMTYtMDgtMjlUMTk6Mzg6MjJaIj4KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgICAgICA8c2FtbDpBdWRpZW5jZT51cm46YXV0aDA6ZmlyZWdsYXNzOnB1dG5hbTwvc2FtbDpBdWRpZW5jZT4KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+CiAgICA8L3NhbWw6Q29uZGl0aW9ucz4KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNi0wOC0yOVQxOTozMzoyMVoiIFNlc3Npb25JbmRleD0iaWQtNmhvZ2s4Sm1XcThoSkhld2FWQ05pU05YbXFMMEx2ZndoeVRTOTZDdSIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxNi0wOC0yOVQyMDozMzoyMloiPgogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+CiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+CiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+CiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+CiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+CiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhzaTp0eXBlPSJ4czpzdHJpbmciPkRlbW9fVXNlckBwdXRuYW0uY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPgogICAgICA8L3NhbWw6QXR0cmlidXRlPgogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ic24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPgogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeHNpOnR5cGU9InhzOnN0cmluZyI+VXNlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9ImdpdmVuTmFtZSIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+CiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5EZW1vPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPgogICAgICA8L3NhbWw6QXR0cmlidXRlPgogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4KICA8L3NhbWw6QXNzZXJ0aW9uPgo8L3NhbWxwOlJlc3BvbnNlPg=='; + const samlResponse = new Buffer(encodedSamlResponse, 'base64').toString(); + var options = { + thumbprint: '5CA6E1202EAFC0A63A5B93A43572EB2376FED309', + checkExpiration: false, + checkDestination: false, + checkRecipient: false, + realm: 'urn:auth0:fireglass:putnam' + }; + var samlp = new Samlp(options, new Saml(options)); + samlp.validateSamlResponse(samlResponse, function (err, profile) { + if (err) return done(err); + expect(profile).to.be.ok; + done(); + }); + }); + + it('should return profile when saml response is encrypted', function(done){ + var encodedSamlResponse = fs.readFileSync(__dirname + '/samples/encoded/samlresponse_encrypted_and_signed.txt').toString(); + const samlResponse = new Buffer(encodedSamlResponse, 'base64').toString(); + var options = { + decryptionKey: fs.readFileSync(__dirname + '/test-auth0.key'), + thumbprint: '119B9E027959CDB7C662CFD075D9E2EF384E445F', + checkExpiration: false, + checkDestination: false, + checkRecipient: false, + realm: 'urn:auth0:login0:simplephp' + }; + var samlp = new Samlp(options, new Saml(options)); + samlp.validateSamlResponse(samlResponse, function (err, profile) { + if (err) return done(err); + expect(profile).to.be.ok; + done(); + }); + }); + + it('should accept the signature when the saml response has an embedded XML assertion', function(done){ + var encodedSamlResponse = fs.readFileSync(__dirname + '/samples/encoded/samlresponse_encoded_xml.txt').toString(); + var cert = fs.readFileSync(__dirname + '/test-auth0-2.cer').toString(); + const samlResponse = new Buffer(encodedSamlResponse, 'base64').toString(); + var options = { + cert: cert, + checkExpiration: false, + checkDestination: false, + checkRecipient: false, + checkAudience: false, + checkSPNameQualifier: false + }; + var samlp = new Samlp(options, new Saml(options)); + + samlp.validateSamlResponse(samlResponse, function (err, profile) { + if (err) return done(err); + expect(profile).to.be.ok; + done(); + }); + }); + + it('should digest has an extra space', function(done){ + var encodedSamlResponse = fs.readFileSync(__dirname + '/samples/encoded/samlresponse_extraspace.txt').toString(); + var cert = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + const samlResponse = new Buffer(encodedSamlResponse, 'base64').toString(); + var options = { + cert: cert, + checkExpiration: false, + checkDestination: false, + checkRecipient: false, + checkAudience: false, + checkSPNameQualifier: false + }; + var samlp = new Samlp(options, new Saml(options)); + samlp.validateSamlResponse(samlResponse, function (err, profile) { + if (err) return done(err); + expect(profile).to.be.ok; + done(); + }); + }); + + it('should return profile for IBM saml response', function(done){ + var cert = fs.readFileSync(__dirname + '/test-auth0.cer'); + var encodedSamlResponse = 'PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9zYWZhcmlqdi5hdXRoMC5jb20vbG9naW4vY2FsbGJhY2s/Y29ubmVjdGlvbj1JQk0tUHJvZCIgSUQ9InBmeDA4NzM0OGQ3LTU0NGUtYjM1OS03MDRlLTA3NjhlZmZjNDllZiIgSW5SZXNwb25zZVRvPSJfMjNkMzQ3YWQzMmFiYmQyODhmYmMiIElzc3VlSW5zdGFudD0iMjAxNi0wOS0wNlQxOToxOTo0NloiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwczovL3czaWQuc3NvLmlibS5jb20vYXV0aC9zcHMvc2FtbGlkcC9zYW1sMjA8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmU+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDA4NzM0OGQ3LTU0NGUtYjM1OS03MDRlLTA3NjhlZmZjNDllZiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+bktJSmFnRWhZMG53aldmMmVUTVVweTdCL084PTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5uUW9MdGZsclNhVnBWNkZRRXVPUm8vZHptK3ZOOHFBVTRkakpPeEVYSGpzem1yUVkwVEF2UE5TNzZML2YvbG1aTWJ2a2ZnNVovcFpCbExmcm1zaUJScXE3RUtySHpKcEdVMzllMmZyT2pZOE1hSDk1ZFdoMFN6dEg0cnZOMmNVb3pxT3hGVkhNZmJLVkpUbHRYZ3ZWMWFkYWlTalRpR2lhQURTb1ZUNFAxeWR5QklsZE50N3c4dHlGWU1YMExPa08zMUZGOTNYR0V5WXdSbllGVzBYekxYNEFuRms1amtsa0Y0cGdIbHcvNDNwelJMSmNXMUYra3BMTWJhMTdjZzdYQVZ6d2J5Yzg1R3JMS1czaWpkQ1dFUlcxVERtMWpjd2hDeEZnR2NGcVAwWWFMd0lsZzlDZzA1QTQzV1ZFQnA4VkJSanEray9zNFl1czNLem56V2xxN3c9PTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUVEekNDQXZlZ0F3SUJBZ0lKQUxyOUh3Z3JRN0dlTUEwR0NTcUdTSWIzRFFFQkJRVUFNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaREFlRncweE1qRXlNamt4TlRNd05EZGFGdzB4TXpBeE1qZ3hOVE13TkRkYU1HSXhHREFXQmdOVkJBTVREMkYxZEdnd0xtRjFkR2d3TG1OdmJURVNNQkFHQTFVRUNoTUpRWFYwYURBZ1RFeERNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1YyRnphR2x1WjNSdmJqRVFNQTRHQTFVRUJ4TUhVbVZrYlc5dVpEQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1aaVZtTkhpWExsZHJnYlM1ME9OTk9IN3BKMnpnNk9jU01rWVpHRFpKYk9aL1Rxd2F1QzZKT25JNyt4dGtQSnNRSFpTRkpzNFUwc3JqWkt6RENtYXoyakxBSkRTaFAyamFYbHJraTE2bkRMUEUvL0lHQWczQkpndVNtQkNXcERiU205MlY5aFNzRStNaHg2YkRhSml3OHlRK1E4aVNtMGFUUVp0cDZPNElDTXUwMEVTZGg5TkpxSUVDRUx2UDMxQURWMVhoajdJYnl5VlBERnhNdjNvbDVCeVNFOXd3d09GVXEvd3Y3WHo5TFJpVWpVelBPK0xxM09NM28vdUNEYms3akQ3WHJHVXVPeWRBTEQ4VUxzWHA0RXVETytuRmJlWEIvaUtuZFp5bnVWS29raXJ5d2wybkQySVAwL3luY2RMUVo4QnlJeXFQM0c4MmZxL2w4cDdBc0NBd0VBQWFPQnh6Q0J4REFkQmdOVkhRNEVGZ1FVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBZd2daUUdBMVVkSXdTQmpEQ0JpWUFVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBhaFpxUmtNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaSUlKQUxyOUh3Z3JRN0dlTUF3R0ExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRnJYSWhDeTRUNGVHcmlrYjBSMndIdi91UzU0OHIzcFp5QlYwQ0RiY1J3QXRibnBKTXZrR0ZxS1ZwNHBteW9JRFNWTksvaitzTEVzaEIyMFhmdGV6SFp5UkpiQ1VidEt2WFE2RnN4b2VaTWxOMElUWUtUYW9CWktoVXh4ajkwb3RBaE5DNThxd0dVUHF0Mkxld0poSHlMdWNLa0dKMW1RM2I1eEtaNTMyVG91Zm91SDlWTGhpZzNIMUtueFdvL3pNRDZLZThjQ2s2cU85aHR1aEkwNnMzR1FHUzFRV1F0QW1tMTdDNlRmS2dEd1FGWndocUhVVVpud0tSSDhnVTZPZ1pzdmhnVjFCN0g1bWpaY3U1N0tNaURCZWtVOU1FWTBEQ1ZUTjNXa21jVElJNjY4ekxzSnJrTlg2UEVmY2sxQU1CYlZFNnBFVUtjV3dxM3VhTHZsQVVvPTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiBJRD0icGZ4YzE0MmE2ZjctZGY4ZC0yMTMxLTVkZDEtOGIyYTI4NWEyMWViIiBJc3N1ZUluc3RhbnQ9IjIwMTYtMDktMDZUMTk6MTk6NDZaIiBWZXJzaW9uPSIyLjAiPjxzYW1sOklzc3VlciBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSI+aHR0cHM6Ly93M2lkLnNzby5pYm0uY29tL2F1dGgvc3BzL3NhbWxpZHAvc2FtbDIwPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlPgogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNwZnhjMTQyYTZmNy1kZjhkLTIxMzEtNWRkMS04YjJhMjg1YTIxZWIiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPlV6VVZTKzZYUlBoS1VLN2N3M2RpaW9mWVNUZz08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+dVhYRWpvOENqcWRiRHMyTUVXb29BYnVmdjFockM1QktYdW9ZdVMvOVoxZXFoMXZaZGdWb2dxejJ5enoyWVN0elpvbEI1NXpMOUViSHVISjhqcThGdzZ5RERtN2lnQjJRNnBlajA4RlRya3pCbnQ3NDg1d0tUY1RVSmRFSDd0REpVUjVpYm0yRVNXRlRYaWg3RmlBYjVCczlOQlgra0sxTUpCcEtFUE9ybHFCL0lKYndlMGJRY1FiUzZPU2ZjaVJpUDdWcnczN3hCKzJ0bTVRbGdzeTd1SlhwSGFCK2pFckZUM0VkeWVrYVMrS2dWbUU2Zjk4OUt5OG45YitXMXAxTGJNUUp6NStlVXNhSlZQcXQ2U244U0R1S3QrdXdaV1RNTnRUSjR0WjVoM2t1SEFMOXNwdGhsZGZJN3NVRkF5UnI0S0kyM1lFKzJsSzYycGYvdnVleGFRPT08L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRHpDQ0F2ZWdBd0lCQWdJSkFMcjlId2dyUTdHZU1BMEdDU3FHU0liM0RRRUJCUVVBTUdJeEdEQVdCZ05WQkFNVEQyRjFkR2d3TG1GMWRHZ3dMbU52YlRFU01CQUdBMVVFQ2hNSlFYVjBhREFnVEV4RE1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQk1LVjJGemFHbHVaM1J2YmpFUU1BNEdBMVVFQnhNSFVtVmtiVzl1WkRBZUZ3MHhNakV5TWpreE5UTXdORGRhRncweE16QXhNamd4TlRNd05EZGFNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNWmlWbU5IaVhMbGRyZ2JTNTBPTk5PSDdwSjJ6ZzZPY1NNa1laR0RaSmJPWi9UcXdhdUM2Sk9uSTcreHRrUEpzUUhaU0ZKczRVMHNyalpLekRDbWF6MmpMQUpEU2hQMmphWGxya2kxNm5ETFBFLy9JR0FnM0JKZ3VTbUJDV3BEYlNtOTJWOWhTc0UrTWh4NmJEYUppdzh5UStROGlTbTBhVFFadHA2TzRJQ011MDBFU2RoOU5KcUlFQ0VMdlAzMUFEVjFYaGo3SWJ5eVZQREZ4TXYzb2w1QnlTRTl3d3dPRlVxL3d2N1h6OUxSaVVqVXpQTytMcTNPTTNvL3VDRGJrN2pEN1hyR1V1T3lkQUxEOFVMc1hwNEV1RE8rbkZiZVhCL2lLbmRaeW51Vktva2lyeXdsMm5EMklQMC95bmNkTFFaOEJ5SXlxUDNHODJmcS9sOHA3QXNDQXdFQUFhT0J4ekNCeERBZEJnTlZIUTRFRmdRVUhJMnJVWGVCalR2MXpBbGxhUEdySEZjRUswWXdnWlFHQTFVZEl3U0JqRENCaVlBVUhJMnJVWGVCalR2MXpBbGxhUEdySEZjRUswYWhacVJrTUdJeEdEQVdCZ05WQkFNVEQyRjFkR2d3TG1GMWRHZ3dMbU52YlRFU01CQUdBMVVFQ2hNSlFYVjBhREFnVEV4RE1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQk1LVjJGemFHbHVaM1J2YmpFUU1BNEdBMVVFQnhNSFVtVmtiVzl1WklJSkFMcjlId2dyUTdHZU1Bd0dBMVVkRXdRRk1BTUJBZjh3RFFZSktvWklodmNOQVFFRkJRQURnZ0VCQUZyWEloQ3k0VDRlR3Jpa2IwUjJ3SHYvdVM1NDhyM3BaeUJWMENEYmNSd0F0Ym5wSk12a0dGcUtWcDRwbXlvSURTVk5LL2orc0xFc2hCMjBYZnRlekhaeVJKYkNVYnRLdlhRNkZzeG9lWk1sTjBJVFlLVGFvQlpLaFV4eGo5MG90QWhOQzU4cXdHVVBxdDJMZXdKaEh5THVjS2tHSjFtUTNiNXhLWjUzMlRvdWZvdUg5VkxoaWczSDFLbnhXby96TUQ2S2U4Y0NrNnFPOWh0dWhJMDZzM0dRR1MxUVdRdEFtbTE3QzZUZktnRHdRRlp3aHFIVVVabndLUkg4Z1U2T2dac3ZoZ1YxQjdINW1qWmN1NTdLTWlEQmVrVTlNRVkwRENWVE4zV2ttY1RJSTY2OHpMc0pya05YNlBFZmNrMUFNQmJWRTZwRVVLY1d3cTN1YUx2bEFVbz08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OnVuc3BlY2lmaWVkIiBOYW1lUXVhbGlmaWVyPSJodHRwczovL3czaWQuc3NvLmlibS5jb20vYXV0aC9zcHMvc2FtbGlkcC9zYW1sMjAiIFNQTmFtZVF1YWxpZmllcj0idXJuOmF1dGgwOnNhZmFyaWp2OklCTS1Qcm9kIj51dWlkNmRkOTc0MzUtMDE1NC0xODZhLTk3MWYtZWUxYzhlZmFiZGRlPC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJfMjNkMzQ3YWQzMmFiYmQyODhmYmMiIE5vdE9uT3JBZnRlcj0iMjAxNi0wOS0wNlQxOToyOTo0NloiIFJlY2lwaWVudD0iaHR0cHM6Ly9zYWZhcmlqdi5hdXRoMC5jb20vbG9naW4vY2FsbGJhY2s/Y29ubmVjdGlvbj1JQk0tUHJvZCIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTA5LTA2VDE5OjE4OjQ2WiIgTm90T25PckFmdGVyPSIyMDE2LTA5LTA2VDE5OjI5OjQ2WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT51cm46YXV0aDA6c2FmYXJpanY6SUJNLVByb2Q8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE2LTA5LTA2VDE5OjE5OjQ2WiIgU2Vzc2lvbkluZGV4PSJ1dWlkZWVmZmMwLTAxNTctMWI3Mi1hZmYwLTg5NGFiMDhmODRkOSIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxNi0wOS0wN1QwODoxOTo0NloiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9IkVtYWlsQWRkcmVzcyIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+Y29ybmVsLnBvcGFAcm8uaWJtLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJVc2VySUQiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPlk5QzRCTTgyNjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==' + const samlResponse = new Buffer(encodedSamlResponse, 'base64').toString(); + var options = { + cert: cert, + thumbprint: '5CA6E1202EAFC0A63A5B93A43572EB2376FED309', + checkExpiration: false, + checkDestination: false, + checkRecipient: false, + realm: 'urn:auth0:safarijv:IBM-Prod' + }; + var samlp = new Samlp(options, new Saml(options)); + samlp.validateSamlResponse(samlResponse, function (err, profile) { + if (err) return done(err); + expect(profile).to.be.ok; + done(); + }); + }); + }); + + it('should reject signature wrapped response', function(done) { + var encodedSamlResponse = 'PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIgeG1sbnM6ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOng1MDA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm9maWxlczphdHRyaWJ1dGU6WDUwMCIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgRGVzdGluYXRpb249Imh0dHBzOi8vYXZpbGxhY2hsYWIuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249Q0hPUCIgSUQ9InBmeDJiYTM1MDM4LTdmZmYtZjljMC1jOWJjLTE0NjJlMTQ1NWE3NiIgSXNzdWVJbnN0YW50PSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHA6Ly9jaWRtZmVkLmNob3AuZWR1L29hbS9mZWQ8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmU+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDJiYTM1MDM4LTdmZmYtZjljMC1jOWJjLTE0NjJlMTQ1NWE3NiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+d0ZLLy9YN0dBdzVQQlFIbnRQV2I4T1RoWkVFPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT50SWI4WjZPV3ExVDBzd3M2SkZkQWJVUjZGRUJrM0k3TmtYZ2s1d0N0NDJ0TWpQcTM0M2o4YWoxeHdKcXNiWXZMVHZBdHhFZ21vaGd4dmNKN29BRGlxWEJnRFE2SEpOeGUzVTZxM05HTzZRN1hobXRITUZOK2JmK0JsVDdIbGw2TWExMUJmWU5pNnJLblJPcUpUTDZlem01M2pMTm5xazlFbi9HWXdjQUttR0kxQzF4bEo5Y1FEdUh6QTZ3NTdUZXhkQU9YbkJWTWk1MG9Bb0FHOHRhVURXdHBwUXdmdXVDRitEN056NVFvVU5VS0UvRXh0VGpyaUJnMDRSWHY2Z0ZUS3FZYmViNHFETUlxZjZoZ3BWZDF4cm9aaXBHZlFodUhvY2pvVUtRU2ZTUDhCRFlEVFpveFZJaUVCVUhQOFJSSzVYb2Y0NXgwK2ZZajErTzdrZzhWcEE9PTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUVEekNDQXZlZ0F3SUJBZ0lKQUxyOUh3Z3JRN0dlTUEwR0NTcUdTSWIzRFFFQkJRVUFNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaREFlRncweE1qRXlNamt4TlRNd05EZGFGdzB4TXpBeE1qZ3hOVE13TkRkYU1HSXhHREFXQmdOVkJBTVREMkYxZEdnd0xtRjFkR2d3TG1OdmJURVNNQkFHQTFVRUNoTUpRWFYwYURBZ1RFeERNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1YyRnphR2x1WjNSdmJqRVFNQTRHQTFVRUJ4TUhVbVZrYlc5dVpEQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1aaVZtTkhpWExsZHJnYlM1ME9OTk9IN3BKMnpnNk9jU01rWVpHRFpKYk9aL1Rxd2F1QzZKT25JNyt4dGtQSnNRSFpTRkpzNFUwc3JqWkt6RENtYXoyakxBSkRTaFAyamFYbHJraTE2bkRMUEUvL0lHQWczQkpndVNtQkNXcERiU205MlY5aFNzRStNaHg2YkRhSml3OHlRK1E4aVNtMGFUUVp0cDZPNElDTXUwMEVTZGg5TkpxSUVDRUx2UDMxQURWMVhoajdJYnl5VlBERnhNdjNvbDVCeVNFOXd3d09GVXEvd3Y3WHo5TFJpVWpVelBPK0xxM09NM28vdUNEYms3akQ3WHJHVXVPeWRBTEQ4VUxzWHA0RXVETytuRmJlWEIvaUtuZFp5bnVWS29raXJ5d2wybkQySVAwL3luY2RMUVo4QnlJeXFQM0c4MmZxL2w4cDdBc0NBd0VBQWFPQnh6Q0J4REFkQmdOVkhRNEVGZ1FVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBZd2daUUdBMVVkSXdTQmpEQ0JpWUFVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBhaFpxUmtNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaSUlKQUxyOUh3Z3JRN0dlTUF3R0ExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRnJYSWhDeTRUNGVHcmlrYjBSMndIdi91UzU0OHIzcFp5QlYwQ0RiY1J3QXRibnBKTXZrR0ZxS1ZwNHBteW9JRFNWTksvaitzTEVzaEIyMFhmdGV6SFp5UkpiQ1VidEt2WFE2RnN4b2VaTWxOMElUWUtUYW9CWktoVXh4ajkwb3RBaE5DNThxd0dVUHF0Mkxld0poSHlMdWNLa0dKMW1RM2I1eEtaNTMyVG91Zm91SDlWTGhpZzNIMUtueFdvL3pNRDZLZThjQ2s2cU85aHR1aEkwNnMzR1FHUzFRV1F0QW1tMTdDNlRmS2dEd1FGWndocUhVVVpud0tSSDhnVTZPZ1pzdmhnVjFCN0g1bWpaY3U1N0tNaURCZWtVOU1FWTBEQ1ZUTjNXa21jVElJNjY4ekxzSnJrTlg2UEVmY2sxQU1CYlZFNnBFVUtjV3dxM3VhTHZsQVVvPTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiBJRD0iaWQtWS1Sd0hpNlJQOGpNVVI4a3IxRlZ6SHVOdmJ1ck9JZUs2d0dwTmpkLSIgSXNzdWVJbnN0YW50PSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHA6Ly9jaWRtZmVkLmNob3AuZWR1L29hbS9mZWQ8L3NhbWw6SXNzdWVyPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6dW5zcGVjaWZpZWQiPkhhbmtlZUpAZW1haWwuY2hvcC5lZHU8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTYtMDgtMTBUMTk6MjU6MjhaIiBSZWNpcGllbnQ9Imh0dHBzOi8vYXZpbGxhY2hsYWIuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249Q0hPUCIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgTm90T25PckFmdGVyPSIyMDE2LTA4LTEwVDE5OjI1OjI4WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT51cm46YXV0aDA6YXZpbGxhY2hsYWI6Q0hPUDwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTYtMDgtMTBUMTk6MjA6MjhaIiBTZXNzaW9uSW5kZXg9ImlkLXZNVy0zckstdlJlb2V1T2Q1QXRWOEpiLVFRNENtUTB6RzQ1ZlRZSjEiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTYtMDgtMTBUMjA6MjA6MjhaIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+TERBUFNjaGVtZV9HUklOPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4='; + var cert = fs.readFileSync(__dirname + '/test-auth0.cer').toString(); + var buffer = new Buffer(encodedSamlResponse, 'base64').toString(); + var xml = buffer.toString(); + //Create version of response without signature + + var stripped = xml + .replace(//, "") + .replace(//, "") + .replace(/<\/samlp:Response>/, "") + .replace(//, "") + .replace(/<\/saml:Assertion>/, ""); + //Create version of response with altered IDs and new username + var outer = xml + .replace(/assertion" ID="_[0-9a-f]{3}/g, 'assertion" ID="_000') + .replace("HankeeJ@email.chop.edu", "admin@esaml2.com"); + //Put stripped version under SubjectConfirmationData of modified version + var xmlWrapped = outer.replace(/]*\/>/, "" + stripped.replace('', "") + ""); + + var newWrap = new Buffer(xmlWrapped, 'base64').toString(); + var options = { + cert: cert, + checkExpiration: false, + checkDestination: false, + checkRecipient: false, + checkAudience: false, + checkSPNameQualifier: false + }; + + var samlp = new Samlp(options, new Saml(options)); + samlp.validateSamlResponse(xmlWrapped, function (err, profile) { + expect(err).to.be.ok; + expect(err.name).to.equals('Error'); + expect(err.message).to.equal('Signature check errors: invalid signature: for uri #pfx2ba35038-7fff-f9c0-c9bc-1462e1455a76 calculated digest is Ayvfyx1bD/XCEn890UHBtv6jkrA= but the xml to validate supplies digest wFK//X7GAw5PBQHntPWb8OThZEE='); + done(); + }); + }); + + describe('getSamlRequestParams', function(){ + before(function(){ + this.samlp = new Samlp({}); + }); + + it('should error if the identityProviderUrl is not a string', function(done) { + var options = {identityProviderUrl: 42}; + this.samlp.getSamlRequestParams(options, function(err, result) { + expect(err).to.be.an('error'); + expect(err.message).to.equal('Invalid identity provider URL: 42'); + expect(result).to.not.exist; + done(); + }); + }); + + it('should error if the identityProviderUrl is a string but not a URL', function(done) { + var options = {identityProviderUrl: 'not a URL'}; + this.samlp.getSamlRequestParams(options, function(err, result) { + expect(err).to.be.an('error'); + expect(err.message).to.equal('Invalid identity provider URL: "not a URL"'); + expect(result).to.not.exist; + done(); + }); + }); + + it('should be OK if the identityProviderUrl is a URL', function(done) { + var relayState = 'foobar'; + var options = {identityProviderUrl: `https://example.com?RelayState=${relayState}`}; + this.samlp.getSamlRequestParams(options, function(err, result) { + expect(err).to.not.exist; + expect(result).to.have.property('RelayState', relayState); + done(); + }); + }); + + it('should use providername option', function(done) { + var samlp = new Samlp({ + identityProviderUrl: server.identityProviderUrl, + requestTemplate: '@@Issuer@@', + protocol: 'samlp', + providerName: 'Some name' + }); + samlp.getSamlRequestParams({}, function(err, params) { + expect(params).to.have.property('SAMLRequest'); + var SAMLRequest = params.SAMLRequest; + + zlib.inflateRaw(new Buffer(SAMLRequest, 'base64'), function (err, buffer) { + if (err) return done(err); + + var request = buffer.toString(); + var doc = new xmldom.DOMParser().parseFromString(request); + + expect(doc.documentElement.getAttribute('ProviderName')) + .to.equal('Some name'); + + done(); + }); + }); + }); + + it('should explode with invalid template', function(done) { + var samlp = new Samlp({ + identityProviderUrl: server.identityProviderUrl, + requestTemplate: '@@Issuer@@@@.0">@@Issuer@@', + protocol: 'samlp', + providerName: 'Some name' + }); + + samlp.getSamlRequestParams({}, function(err, params) { + expect(err).not.to.be.null; + expect(err.message).to.match(/Malformed template/); + done(); + }); + }); + + + describe('signing', function () { + describe('HTTP-POST or HTTP-Redirect without deflate encoding', function () { + it('should error if the requestTemplate is malformed', function (done) { + var options = { + protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + signingKey: { + key: fs.readFileSync(__dirname + '/test-auth0.key'), + cert: fs.readFileSync(__dirname + '/test-auth0.pem') + }, + identityProviderUrl: 'https://example.com?RelayState=foo', + requestTemplate: '@@Issuer@@urn:au:qld:gov:authn:names:SAML:2.0:ac:AAL2urn:au:qld:gov:authn:names:SAML:2.0:attributes:FamilyName,MiddleName,GivenName,email,DateOfBirth', + protocol: 'samlp', + deflate: false, + providerName: 'Some name', + signingKey: { + key: fs.readFileSync(__dirname + '/test-auth0.key'), + cert: fs.readFileSync(__dirname + '/test-auth0.pem') + } + }); + + samlp.getSamlRequestParams({}, function(err, params) { + if (err){ return done(err); } + expect(params).to.have.property('SAMLRequest'); + var request = new Buffer(params.SAMLRequest, 'base64').toString(); + var doc = new xmldom.DOMParser().parseFromString(request); + + var issuer = doc.documentElement.getElementsByTagName('saml:Issuer'); + expect(issuer[0].nextSibling.nodeName).to.equal('Signature'); + + done(); + }); + }); + }); + }); + }); + + describe('getSamlRequestUrl', function(){ + before(function(){ + this.samlp = new Samlp({}); + }); + it('should be OK if the identityProviderUrl is a URL', function(done) { + var options = {identityProviderUrl: 'https://example.com'}; + this.samlp.getSamlRequestUrl(options, function(err, result) { + expect(err).to.not.exist; + expect(result).to.match(/^https:\/\/example.com\?SAMLRequest=.*&RelayState=.*/); + done(); + }); + }); + it('should error if the identityProviderUrl is not a URL', function(done) { + var options = {identityProviderUrl: null}; + this.samlp.getSamlRequestUrl(options, function(err, result) { + expect(err).to.be.an('error'); + expect(err.message).to.equal('Invalid identity provider URL: null'); + expect(result).to.not.exist; + done(); + }); + }); + }); + + describe('getSamlStatus', function(){ + before(function(){ + this.samlp = new Samlp({}); + }); + + it('should get result without subcode', function(){ + var samlpResponse = 'urn:fixture-testsome messagesome details'; + + var result = this.samlp.getSamlStatus(samlpResponse); + + expect(result.code).to.equal('urn:oasis:names:tc:SAML:2.0:status:Responder'); + expect(result.subCode).to.be.undefined; + expect(result.message).to.equal('some message'); + expect(result.detail).to.equal('some details'); + }); + + it('should get result with sucode', function(){ + var samlpResponse = 'urn:fixture-testsome messagesome details'; + + var result = this.samlp.getSamlStatus(samlpResponse); + expect(result.code).to.equal('urn:oasis:names:tc:SAML:2.0:status:Responder'); + expect(result.subCode).to.equal('urn:oasis:names:tc:SAML:2.0:status:AuthNFailed'); + expect(result.message).to.equal('some message'); + expect(result.detail).to.equal('some details'); + }); + + it('should get result without details', function(){ + var samlpResponse = 'urn:fixture-testsome message'; + + var result = this.samlp.getSamlStatus(samlpResponse); + expect(result.code).to.equal('urn:oasis:names:tc:SAML:2.0:status:Responder'); + expect(result.subCode).to.equal('urn:oasis:names:tc:SAML:2.0:status:AuthNFailed'); + expect(result.message).to.equal('some message'); + expect(result.detail).to.be.undefined; + }); + + it('should get result without message', function(){ + var samlpResponse = 'urn:fixture-test'; + + var result = this.samlp.getSamlStatus(samlpResponse); + expect(result.code).to.equal('urn:oasis:names:tc:SAML:2.0:status:Responder'); + expect(result.subCode).to.equal('urn:oasis:names:tc:SAML:2.0:status:AuthNFailed'); + expect(result.message).be.undefined; + expect(result.detail).be.undefined; + }); + + it('should get result with status code only', function(){ + var samlpResponse = 'urn:fixture-test'; + + var result = this.samlp.getSamlStatus(samlpResponse); + expect(result.code).to.equal('urn:oasis:names:tc:SAML:2.0:status:Responder'); + expect(result.subCode).be.undefined; + expect(result.message).be.undefined; + expect(result.detail).be.undefined; + }); + }); + + describe('decodeResponse', function(){ + it('should decode the SAML response using default settings', function() { + const xml = 'http://idp.example.com/metadata.phphttp://idp.example.com/metadata.php_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7http://sp.example.com/demo1/metadata.phpurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtest@exåmple.com'; + const SAMLResponse = new Buffer(xml, 'binary').toString('base64') + const samlp = new Samlp({}); + const response = samlp.decodeResponse({ body: { SAMLResponse: SAMLResponse } }); + const doc = new xmldom.DOMParser().parseFromString(response); + const attributes = doc.documentElement.getElementsByTagName('saml:Attribute'); + expect(attributes.length).to.equal(1); + expect(attributes[0].getAttribute('Name')).to.equal('mail'); + // wrongly decoded + expect(attributes[0].textContent.trim()).to.equal('test@ex�mple.com'); + }); + + it('should decode the SAML response using the defined settings', function() { + const xml = 'http://idp.example.com/metadata.phphttp://idp.example.com/metadata.php_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7http://sp.example.com/demo1/metadata.phpurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtest@exåmple.com'; + const SAMLResponse = new Buffer(xml, 'binary').toString('base64') + const samlp = new Samlp({ default_encoding: 'ISO-8859-1' }); + const response = samlp.decodeResponse({ body: { SAMLResponse: SAMLResponse } }); + const doc = new xmldom.DOMParser().parseFromString(response); + const attributes = doc.documentElement.getElementsByTagName('saml:Attribute'); + expect(attributes.length).to.equal(1); + expect(attributes[0].getAttribute('Name')).to.equal('mail'); + expect(attributes[0].textContent.trim()).to.equal('test@exåmple.com'); + }); + + it('should decode the SAML response using default settings when invalid encoding', function() { + const xml = 'http://idp.example.com/metadata.phphttp://idp.example.com/metadata.php_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7http://sp.example.com/demo1/metadata.phpurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtest@exåmple.com'; + const SAMLResponse = new Buffer(xml, 'binary').toString('base64') + const samlp = new Samlp({ default_encoding: 'foo' }); + const response = samlp.decodeResponse({ body: { SAMLResponse: SAMLResponse } }); + const doc = new xmldom.DOMParser().parseFromString(response); + const attributes = doc.documentElement.getElementsByTagName('saml:Attribute'); + expect(attributes.length).to.equal(1); + expect(attributes[0].getAttribute('Name')).to.equal('mail'); + // wrongly decoded + expect(attributes[0].textContent.trim()).to.equal('test@ex�mple.com'); + }); + + it('should get the encoding from the xml tag and decode with the correct encoding', function() { + const xml = 'http://idp.example.com/metadata.phphttp://idp.example.com/metadata.php_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7http://sp.example.com/demo1/metadata.phpurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtest@exåmple.com'; + const SAMLResponse = new Buffer(xml, 'binary').toString('base64') + const samlp = new Samlp({}); + const response = samlp.decodeResponse({ body: { SAMLResponse: SAMLResponse } }); + const doc = new xmldom.DOMParser().parseFromString(response); + const attributes = doc.documentElement.getElementsByTagName('saml:Attribute'); + expect(attributes.length).to.equal(1); + expect(attributes[0].getAttribute('Name')).to.equal('mail'); + expect(attributes[0].textContent.trim()).to.equal('test@exåmple.com'); + }); + + it('should get the encoding from the xml tag and don\'t encode again because it is not valid', function() { + const xml = 'http://idp.example.com/metadata.phphttp://idp.example.com/metadata.php_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7http://sp.example.com/demo1/metadata.phpurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtest@exåmple.com'; + const SAMLResponse = new Buffer(xml, 'binary').toString('base64') + const samlp = new Samlp({}); + const response = samlp.decodeResponse({ body: { SAMLResponse: SAMLResponse } }); + const doc = new xmldom.DOMParser().parseFromString(response); + const attributes = doc.documentElement.getElementsByTagName('saml:Attribute'); + expect(attributes.length).to.equal(1); + expect(attributes[0].getAttribute('Name')).to.equal('mail'); + // wrongly decoded + expect(attributes[0].textContent.trim()).to.equal('test@ex�mple.com'); + }); + + it('should get the encoding from the xml tag and decode using utf-8', function() { + const xml = 'http://idp.example.com/metadata.phphttp://idp.example.com/metadata.php_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7http://sp.example.com/demo1/metadata.phpurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtest@exåmple.com'; + const SAMLResponse = new Buffer(xml, 'binary').toString('base64') + const samlp = new Samlp({}); + const response = samlp.decodeResponse({ body: { SAMLResponse: SAMLResponse } }); + const doc = new xmldom.DOMParser().parseFromString(response); + const attributes = doc.documentElement.getElementsByTagName('saml:Attribute'); + expect(attributes.length).to.equal(1); + expect(attributes[0].getAttribute('Name')).to.equal('mail'); + // wrongly decoded + expect(attributes[0].textContent.trim()).to.equal('test@ex�mple.com'); + }); + }); +}); diff --git a/test/samples/encoded/samlrequest_signed_differentcert.txt b/test/samples/encoded/samlrequest_signed_differentcert.txt new file mode 100644 index 0000000..55188aa --- /dev/null +++ b/test/samples/encoded/samlrequest_signed_differentcert.txt @@ -0,0 +1 @@ +fZJbc6owFIX%2FCpN3EAEVMmIHEfDaqlCP%2BtKJELkUEkqCl%2F76Uj3O9JyHPmay9l4r%2BVb%2F6VLkwglXLKXEBG1JBgImIY1SEpvgNXBFHTwN%2BgwVeQmtmidkjT9qzLjQzBEGbxcmqCsCKWIpgwQVmEEeQt9azKEiybCsKKchzYFgMYYr3hjZlLC6wJWPq1Ma4tf13AQJ5yWDrVZO45RIDOWYHWkVYimkBRBGjWVKEL%2BlfEhDSjhlVEJNLvlb1%2FqOA4TJyARvynPH80qFFJPAdg%2Fh1fNnGVqpKO3OLkZonUfJ0Nu2Y2t6PdlVPj1RZxVlThywI8rihVH0MuksTQz3sx1Fm2xv5LO9nYSs5KXxfnm364%2FwfMDPWMqn182qHOqpjzR0dncsM6xO1Vs7h860HI97yrB7xHE9dt2loy%2FQu1prie%2FMcuNNL2i6nUdWp%2Fdnk3yekb7dXYhWjFjil%2Br2IC%2Bd%2FexlNF7wS77Zomvo7epFbCuyVx5tq3klYzWeEMYR4SZQ5LYqypqo6IGiQE2FmiKpencPhOXf%2Fx%2Bm5E71N1iHu4jBcRAsxeWLHwBh82hHIwD3LsCbefWjBL%2BvRQ%2FyYPCAd4MmRvgk4kgqrv8R77d%2B2Azup38LOPgC \ No newline at end of file diff --git a/test/samples/encoded/samlresponse_encoded_xml.txt b/test/samples/encoded/samlresponse_encoded_xml.txt new file mode 100644 index 0000000..26319c4 --- /dev/null +++ b/test/samples/encoded/samlresponse_encoded_xml.txt @@ -0,0 +1 @@ +PHNhbWxwOlJlc3BvbnNlIElEPSJfY2ZmNjBhZDYtOTc1Mi00MjllLTlhMzgtOTdmODE3OWNiZDkxIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxOC0wMS0yM1QxODo1NDo1MC42NDlaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9wc2UtYWRkb25zLmF1dGgwLmNvbS9sb2dpbi9jYWxsYmFjaz9jb25uZWN0aW9uPW15LWFkZnMtc2FtbCIgQ29uc2VudD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNvbnNlbnQ6dW5zcGVjaWZpZWQiIEluUmVzcG9uc2VUbz0iXzA1ODZhZGEwNDBkYzdmYjRlMWZmIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48SXNzdWVyIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vYWRmcy5teS5sb2NhbC9hZGZzL3NlcnZpY2VzL3RydXN0PC9Jc3N1ZXI+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIiAvPjwvc2FtbHA6U3RhdHVzPjxBc3NlcnRpb24gSUQ9Il8yMjc5YTI0Mi0yNzY5LTQ4ZmEtOTE2Yy00OTA3ODFiODk5MmYiIElzc3VlSW5zdGFudD0iMjAxOC0wMS0yM1QxODo1NDo1MC42NDlaIiBWZXJzaW9uPSIyLjAiIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48SXNzdWVyPmh0dHA6Ly9hZGZzLm15LmxvY2FsL2FkZnMvc2VydmljZXMvdHJ1c3Q8L0lzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIgLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIgLz48ZHM6UmVmZXJlbmNlIFVSST0iI18yMjc5YTI0Mi0yNzY5LTQ4ZmEtOTE2Yy00OTA3ODFiODk5MmYiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIgLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIiAvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2IiAvPjxkczpEaWdlc3RWYWx1ZT4xWXBmYXBsN2dmdkh1aFEyb0hnT2g0dncrRWp5Tk9Lb0NIVENKaGZhQ2pBPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5vV3dxNnM3NXBoYU9OSUxVazg3alpBT24wczV3dXJ5aEFybVdjSjRKMVBSTWkwdmNiaS9kNHZIVHFoeGt0SG1PY081dlN4T2kwVUdwcU9JVkxoajlScEZDd0tqZ2l5QW9aQnI4bFJycngrRVpkalhlV0t6Zk0xc1pYTWk3ZzZZL05xZmt5aStVT01KZXgwV1ZFbHNtTG9RbGhtcGd6eE5BWnY4QkJ0b0MwbndteUR5VFliMFN0VDhJOVdvMEJOTDRRbWwxT2w0ak1VTUo0S2RudUUyQWwzRituNW1OSE9iQkR0YnV0QjhXZlp4NG1kZjdTTmN0YUlUTkJzUForajU4Q1hRRlFFNnNGdFR3VHEyMmRHbTFFdTVVeTFVMldqc2pGUUpTcGFQTzloVE82eVJEVkpaaE5vQkN6NVBuUkhENkJLNmtGaU9TVVNmRWdyNVdEWU1CRkE9PTwvZHM6U2lnbmF0dXJlVmFsdWU+PEtleUluZm8geG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUMxakNDQWI2Z0F3SUJBZ0lRR1YydzJ1UGNmWkJHWXJGZkdod2JTREFOQmdrcWhraUc5dzBCQVFzRkFEQW5NU1V3SXdZRFZRUURFeHhCUkVaVElGTnBaMjVwYm1jZ0xTQmhaR1p6TG0xNUxteHZZMkZzTUI0WERURTNNRFl4TlRFNU5EUXlOVm9YRFRFNE1EWXhOVEU1TkRReU5Wb3dKekVsTUNNR0ExVUVBeE1jUVVSR1V5QlRhV2R1YVc1bklDMGdZV1JtY3k1dGVTNXNiMk5oYkRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTjZKcmhSSVlRZUJyRk04YlJuekVBRmlkbStSbkZuaEFoUnY1R0JVeTRGamQrZlEwcm80elZNWm95dytoaHMvaUhsU3QzVU56dVFaYVhVK28wTHBFTytMVUR3MldpYVVPVkQ4SDZQTzE3Rmo5ZVQ4NStyR3pYTkt0WnVsUmpXUHhOTThiUWZjd2hjRXQ0amNZb0s0NllpckNMcHU3ak1kMWJXWTZDL0UwWXM4U0hmTFRiN0FaWXhIbzhFbHNjb2YzbnU2NXpIcllJRXJndUQva0I5RmsxdEttZkN3R08wQmhpOWY4Qjh1K1R1UTZHQTRVVzFOK0ZjQnhmY2o4c2lKODRCUlFadTl4RkRNSGZJQ0lFZkJiVWcrWThQdGFnQ1VFcWE1em9UOVhJWGVUK21oU3JMK0hmZUgxa0tqeFc0MzZIam5ETUNHTHR3bXE3RTdXWHIxZDYwQ0F3RUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUzd0SlJlVkVQaWcrTEx4TmZ2TUszeEdTdlFuS1ZVSThHcHpXUm1IRGJaVldlTGx3R2EweGw4R0pmODAwMjJDZUFWRmRWdzJrR2JoRDdVZWFQc3VBR1Q2d0VrdmNqT1E3QmNIWStqU01PaWlHMEp2RkJsNURvMFhIbnBsWDR0UnhqdVRQWG41YnB1Q3B0U2NYQVdiT0ptZXVlbkR4MFJXNGNkTExXelJ0dUtzcm04SXpLSmpURVQzRm9FMHVmaG1hN2dOcFZYenlTVE4xNEV3QkZta0VtdmI1b0hyU2hUQWpPeHVSMVVjZDFycWl5NjV0aXcyT1duU29RZXU2d0NBTDJlYVdLQmc0TzFTbHJSd3ZwajJsY3VSaGNKdUpUWTBwUm9aeFJ6UElEU2VYM0RreUYrNlFrSytPZXR6TGxlT20rMGRMSXhVZUc1ZFZPOHFyalpDTnZBPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvS2V5SW5mbz48L2RzOlNpZ25hdHVyZT48U3ViamVjdD48TmFtZUlEPnBhYnJvbDAwMTwvTmFtZUlEPjxTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PFN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iXzA1ODZhZGEwNDBkYzdmYjRlMWZmIiBOb3RPbk9yQWZ0ZXI9IjIwMTgtMDEtMjNUMTg6NTk6NTAuNjQ5WiIgUmVjaXBpZW50PSJodHRwczovL3BzZS1hZGRvbnMuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249bXktYWRmcy1zYW1sIiAvPjwvU3ViamVjdENvbmZpcm1hdGlvbj48L1N1YmplY3Q+PENvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE4LTAxLTIzVDE4OjU0OjUwLjY0OVoiIE5vdE9uT3JBZnRlcj0iMjAxOC0wMS0yM1QxOTo1NDo1MC42NDlaIj48QXVkaWVuY2VSZXN0cmljdGlvbj48QXVkaWVuY2U+dXJuOmF1dGgwOnBzZS1hZGRvbnM6bXktYWRmcy1zYW1sPC9BdWRpZW5jZT48L0F1ZGllbmNlUmVzdHJpY3Rpb24+PC9Db25kaXRpb25zPjxBdHRyaWJ1dGVTdGF0ZW1lbnQ+PEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zdXJuYW1lIj48QXR0cmlidXRlVmFsdWU+QWJyb2w8L0F0dHJpYnV0ZVZhbHVlPjwvQXR0cmlidXRlPjxBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL2NvbS5maWNjLmNuaGluZC5jbXMvY3VzdG9tcXVlcnkiPjxBdHRyaWJ1dGVWYWx1ZT4mbHQ7Y2xhaW0mZ3Q7Jmx0O2tleSZndDtjb3VudHJ5Jmx0Oy9rZXkmZ3Q7Jmx0O3ZhbHVlJmd0O1VuaXRlZCBTdGF0ZXMmbHQ7L3ZhbHVlJmd0OyZsdDsvY2xhaW0mZ3Q7PC9BdHRyaWJ1dGVWYWx1ZT48L0F0dHJpYnV0ZT48L0F0dHJpYnV0ZVN0YXRlbWVudD48QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE4LTAxLTIzVDE4OjUzOjI0Ljk2MloiIFNlc3Npb25JbmRleD0iXzIyNzlhMjQyLTI3NjktNDhmYS05MTZjLTQ5MDc4MWI4OTkyZiI+PEF1dGhuQ29udGV4dD48QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnQ8L0F1dGhuQ29udGV4dENsYXNzUmVmPjwvQXV0aG5Db250ZXh0PjwvQXV0aG5TdGF0ZW1lbnQ+PC9Bc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file diff --git a/test/samples/encoded/samlresponse_encrypted_and_signed.txt b/test/samples/encoded/samlresponse_encrypted_and_signed.txt new file mode 100644 index 0000000..d242aa1 --- /dev/null +++ b/test/samples/encoded/samlresponse_encrypted_and_signed.txt @@ -0,0 +1 @@ +PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfNDczYmQzMDIxNzdjYzIxNGE3YjRlMWRjNmNkNDE3MDVmZDlhMzI0ZTUzIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNy0xMS0xNVQxNzo1OTowOFoiIERlc3RpbmF0aW9uPSJodHRwczovL2xvZ2luMC5teWF1dGgwLmNvbS9sb2dpbi9jYWxsYmFjayIgSW5SZXNwb25zZVRvPSJfYTIyM2UxZDVjNzEyNTg2MTY2YWYiPjxzYW1sOklzc3Vlcj5odHRwOi8vbG9jYWxob3N0OjgwODAvc2ltcGxlc2FtbC9zYW1sMi9pZHAvbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KICA8ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPgogIDxkczpSZWZlcmVuY2UgVVJJPSIjXzQ3M2JkMzAyMTc3Y2MyMTRhN2I0ZTFkYzZjZDQxNzA1ZmQ5YTMyNGU1MyI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+OERMWmdWUHlXZWFZRkNGaFFXN0lmTVFiWUlBPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5TWmh6WENUNUJVUGpmUnB2aXVRMlZVeUE3ZjNrUnR0SVU2ekNJcE9ZYUprb0NRZ013dnpYSnFEWi83WGtiQlJmZnExRmI0OFQxbk5zRlJya2ZMbldlZ21TTGhFVHFvSFdOaGNWSHdXZ1ZKMlZrb3NvVjl4eXBsN3hrbC9zTWlmWXlnZWFRKy83SlJIcFl3NGxWL3BXU1JGODJGZEpSOHFkNGVIb1BuQVV6WEFzWmJhTGwrdkFYcGhOSVFFOUl6eUF2WGdIWm42NzQrbm1qY1d6Wlo2SGwyMVdLSFgwVzBHV0hSYWwzcWxhWjF3cjlOOUxPb25XVVFOVkYyM1NKSjVyTUczanhwR1hTcm91Z1pNQmI1RWJGQWY1QzU4MytDUkc1YXdTWmJTV2s5TjBGQnM3NnRyVi9FREpTT0VNOFZteUhqeXo1d0IweEtrVDdNUG1hNytrZEE9PTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSURYVENDQWtXZ0F3SUJBZ0lKQUxtVlZ1RFd1NE5ZTUEwR0NTcUdTSWIzRFFFQkN3VUFNRVV4Q3pBSkJnTlZCQVlUQWtGVk1STXdFUVlEVlFRSURBcFRiMjFsTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdIaGNOTVRZeE1qTXhNVFF6TkRRM1doY05ORGd3TmpJMU1UUXpORFEzV2pCRk1Rc3dDUVlEVlFRR0V3SkJWVEVUTUJFR0ExVUVDQXdLVTI5dFpTMVRkR0YwWlRFaE1COEdBMVVFQ2d3WVNXNTBaWEp1WlhRZ1YybGtaMmwwY3lCUWRIa2dUSFJrTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6VUNGb3pnTmIxaDFNMGp6TlJTQ2poT0JuUit1VmJWcGFXZlhZSVIrQWhXRGRFZTVyeVkrQ2dhdk9nOGJmTHlieXpGZGVobFlkRFJna2VkRUIvR2pHOGFKdzA2bDBxRjRqRE9BdzBrRXlnV0N1Mm1jSDdYT3hSdCtZQUgzVFZIYS9IdTFXM1dqemtvYnFxcUxROGdrS1dXTTI3Zk9nQVo2R2llYUpCTjZWQlNNTWNQZXkzSFdMQm1jK1RZSm12MWRiYU8yakhoS2g4cGZLdzBXMTJWTThQMVBJTzhndjRQaHUvdXVKWWllQldLaXhCRXl5MGxIanlpeFlGQ1IxMnhkaDRDQTQ3cTk1OFpSR25uRFVHRlZFMVFoZ1JhY0pDT1o5YmQ1dDltcjhLTGFWQllUQ0pvNUVSRThqeW1hYjVkUHFlNXFLZkpzQ1ppcVdnbGJqVW85dHdJREFRQUJvMUF3VGpBZEJnTlZIUTRFRmdRVXhwdXdjcy9DWVFPeXVpK3IxRyszS3hCTmh4a3dId1lEVlIwakJCZ3dGb0FVeHB1d2NzL0NZUU95dWkrcjFHKzNLeEJOaHhrd0RBWURWUjBUQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBQWlXVUtzLzJ4L3ZpTkNLaTNZNmJsRXVDdEFHaHpPT1o5RWpydko4K0NPSDNSYWczdFZCV3JjQlozL3VoaFBxNWd5OWxxdzRPa3ZFd3M5OS81akZzWDFGSjZNS0JncWZ1eTd5aDVzMVlmTTBBTkhZY3pNbVlwWmVBY1FmMkNHQWFWZndUVGZTbHpOTHNGMmxXL2x5N3lhcEZ6bFlTSkxHb1ZFK09IRXU4ZzVTbE5BQ1VFZmtYdys1RWdoaCtLemxJTjdSNlE3cjJpeFdORkJDL2pXZjdOS1VmSnlYOHFJRzVtZDFZVWVUNkdCVzlCbTIvMS9SaU8yNEpUYVlsZkxkS0s5VFliOHNHNUIrT0xhYjJESW1HOTlDSjI1UmtBY1NvYldORjV6RDBPNmxnT28zY0VkQi9rc0NxM2htdGxDL0RsTFovRDhDSis3VnVablMxclIybmFRPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpFbmNyeXB0ZWRBc3NlcnRpb24+PHhlbmM6RW5jcnlwdGVkRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiIHhtbG5zOmRzaWc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiIFR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI0VsZW1lbnQiPjx4ZW5jOkVuY3J5cHRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNhZXMxMjgtY2JjIi8+PGRzaWc6S2V5SW5mbyB4bWxuczpkc2lnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48eGVuYzpFbmNyeXB0ZWRLZXk+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIi8+PHhlbmM6Q2lwaGVyRGF0YT48eGVuYzpDaXBoZXJWYWx1ZT5PYVJ6eUd3ZTZpbnRIdVF0SytnY1AxMVkydko5UElmRUZaT2x1em5ldEJ1azRMZ2xYSUFZa3dNSXRybURmdktvdG9nZnFvUXBkQTRXeVNTQTljcTJmN0pnN2lpMjMvbUp6YnNlc2RRK0RFL1hsODI5NFI4NHZBL1hxNVlJcUdwS1Zza3FLbTZONElRakRDYStPOWhtRHB5UHk1bnFtS1hNOVNWWEdlb1FwWjgrOFNaZWZWRXQzejFBRStJYUdSUXpOdFhLM0dGbDRLNnpKZlNNMkZPN2dkdVVrUzlDbnY1L2R6ZVNyc1NyVFh2MzhzRTdrZmNDeEE1cGZqclBNZXdrR21GZ0o5Wmc5L1pwTXFkcjU4N0JiS0hhejdWOThaeDUrWWZIU0tGVVk4eWc2bnlMdmc2UGRsa2hGdWF0ZUxnbTRKa1k4Q0xmSFB3SXA5cTBJWmVhMEE9PTwveGVuYzpDaXBoZXJWYWx1ZT48L3hlbmM6Q2lwaGVyRGF0YT48L3hlbmM6RW5jcnlwdGVkS2V5PjwvZHNpZzpLZXlJbmZvPgogICA8eGVuYzpDaXBoZXJEYXRhPgogICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5Na2NtSEJ1Z1ZIK2pXRmdvcklpb2dNZE1HSVBINHAxb0E0aFJCYzJXWGgvcC9tRmxtSXk1ZUcvYmR2VGZnL0puYVEybFZ1SU1uWmpUUkFCMzRWSHM5N1RKMjRLRHpQV2NDOFoxeXBoekVZc0dlKzJmK3ZNYnl6czVVTnF0WnVxdGdpai9FTmpTR1YwbkdiSXAxbE1TQmw2aUZkKzBlOGkvSWo0bWd6ZnlYNFpocHl5UU5vaFpBTE9Gb3Z5Z1dLN2Uxd1BtQ0NHK0t2WFNzZkQzNGNQVTFaa0RleHhTTnh0SThWR29FZzFNTDlkS1czanlqUFhzenJuRVJYbXpZdXBVaU10dWNPUlJ4NjJlWFFKNDdCaStPT3BiQzlMcjdTb2NSWXJYMHBjN0o2WkY0bE1BK0JIczJzUmN1VTFLaGtjT2N2cG5aOC9wSFpOQTFFaE1rK3l0Tm9QZk9kTFBMekJaZmdYbEZ3ZzVtQk9TeEhxVXFsZFBIRndpSWZNZXltbENDZGtxNmRBcUNpYU9XUnBSYVRUbGJBYmlaRk1nZnVtd1Zick9LbzBIcnV1dlpWOVIxV3VTWFNKTm1aMWNqLzlQV0tEWmVjc0xBOHpkL0U3WTV2WUlLR1ROdUZiODRwak41czFoU0MyWW5kWjJxd2JmNjg2bUE4RW1BVGxrYjZ1QnM3ZzgvUjRkS1FXMVlGeGMzMzBFMnQzR3d4blMybVZPS1FHNVVRRGdEVzYrNHJEd28vZWIyV05HNzdDSE45ZUR5TjRxTGZYSW5IWnB2bkdobjJ6TTk2VHpteGVwVXBFTXR6L1RVTTNtMjRBNTU4MUs5a3hLTzFLTnNmUncyZC9ydUFjS0NJekE0ZTBkTWYrSCt0YlVTMG5NSkNDbWsveGhVcldBUktid3NJMUpMRjYwSHN2bHNpY2ZDMGRhTzQvektaanFjbDdDVUk4WEcrYjNHMUVmVW81VjllbUhGN0JLMGNISXBvaWVUc0hURE1Dek5pbjBsTzRSVG1nMHkraXcwSXpvcHFXMlNHdkdqU3UxdnBOSDlLNmVaaFAzeTA0dURiRmQ5d05qUmtBL2VsN3lUWVNSZ09qL21kVXd3NGY0TDJ0d3RxQzJCcE1yQkVHV3F0VmxzMUloeVEzODgrWi9xVVFmWWFndFVNb3pWT3cyOEhnMFdzOFdWTnQvcnFvUEIzUGM1enp2OTBuMWFMQUZ1NGxpOHlaTUNKMnJ0dSszYWtXMlhzRVpJUHJkc2JLbG5rMUpWRXZYdW80Uk9scXEycXV2cE9QWDBkN2oydWhWVTVtV2dNUzg3Qi85RjhpS3k5OVQ1cC9xd3AxK2ROOGhqZ05jeVVMdDJna3dUbGxCcXFUT0NsaUsrYm5saldZU015Wjk4RnM2S0JGYTdkdWVvV3NPYmNvWG85OU9XZ2R4b3QwRmZ3MjJuK1JhSG1TVjhURGpMaHhSUWdnNXBaYXNiS3JmaTBDaElvenVtd0FXMzNKWXBvMWVnZFFFRnMzbEtHUGx1MW5VV3pja09Xcm15RWM5QXNCb2ZDdUZuaUdrZE51VVRmNTBUUFd6YXpJd1FGMUtpTWQrck5NTVg2d0RvSFVTcWZPY3hObUpGQkJGdmJtTWNpREQvak1ZSEsrVkh3cjlYRjlmQmVDcE9ZeXBvNUkwYS9CaVBld2hINXl5OUR6RWZCL1pDckhlYmlvNHhEWmN5aUlDZm5EVEplQ2w5ZWY3Ti8xNzUwS1hqMVJRWk1sV2Jobmw5ODQ1V2NzT0NobDIwd2xkc29JWlp0aXZQaFQ5TmRFQzhrejdJNU8xekRkKzJKdzVDdzNQaThkektxejErb29iMTQxbkttVEcvK1ljV3pGM3RCdExaRTRkSFVpMzh6NldMc0ZHbGtFZW51ZnFBTGxlalRHdXN3WXJjZW9NaVRZeGVQdThoYzRRTFMvY3hQVkQwQ3prQlhFVWJlYXo3WGtvN3RqTmlCZm9KVnJWcWNRb29wcXhoNzNNR3owNXJoZTcwcS81MW9DblBkcUxJcnptb21sRlFsYmNteHpoSDEwWjU1aHdQWTRhUkFna0RMOHFtZUZLSVhMNEthOHEvWEZhc2VVUGhNRUdkTzEyVm1Hbk9Rd1FLWHc5dkxJbEdLNStDWFZsWGt3MnRrZHlHNmtzcG0vKzdzN2k0QnRIMXEyekhQLzhhcWdFWDRta3NUQjVMUHRlMEVXdDVneDZVVTdpaFJFeWpDRFJHdXFWRk9haHZpYkEzQzlBdXZvYzZySzNCQXVrOEJVd2NRSEJvY3NqbWd0L3ZXT2ZxTmFHSG5MMzU1dmc5dlhnNkhETjJ4V20rOVBCd3pxQUVYSmU2cFlSRGxpUHhzOHdrd1J5WElNcjhqc3RVeEZGc1ZVTHcwelhXL214SUdRZ2VLbmZjRm4vYUZqWkNQbi9zT1ltTXdMR2c4YnZjMlMva0gxM1BCdHBscWhURUtGdjd5ZGlaMGVyd3VsS3VtbFpsM1U2dGlrdmFFU3pGeXIxMHU4MW1sUXlZR2NNc1Jaa0NQVmFxU2IzWDNyVEs1NFEvMHdUOTdHWGtSWWlVZDd3UWRKTTVGS3NIVTRqajZsQzcva0txdFdNNW1OZ0htOXVOOFhpclVNNi82VTl5czV3OWc5dG5TbUFLRDF2a0JEa3RwQVVieVB6TS93bUExVGVrSXBYeUpmcVA3SXNJRmt4eUxFcDUrYzMrOURUVm0xQ2NlR2xua2VlMm55cjBqandRUElEY24rZEVBQ1UzTldhb0VJYzllQnJyNUVmVzR5NkVRZk01ZHFlaHN0VStWdlhheXBpb2Y2blVVTGs5ZFFGNjRZWVFPeG1KLzRROHo0T2JZMmtLWFMzRHBSQzhiRlV2WFVCeEtZb1hueTI4V3lOWjI3NlVCRG1JdnhydHdEOU5qZ0xFRTF0ajlSNEpzczB5eHptYlJHTUNZazlFdGVidnczS21ybjcwdGZ6V1hoMWVNN1krczBBaTZwRHM5UkFlSkRpUFBNNlNtaC9ZZDZybXkzZ0phL1BCQmdIUkk1Z0xGRUhRcWdZRDBGSVdxcTJHbVFUb3lOZkQvSEJtSVdXa2FwM2J3R3c5ZnhSdGdNaXFZMUw1ekFtcGRjUThuNVJFb0NVTEZUZG5ueGVzZzFBQXF4eitpcnBpZGZQT3dLYVF5M1FmdkpETHdYcWdPRWFsakNrZEZUSXhwM1pCdkg5QXk5ZE1melhDdzhsVGF3d3lwbERNK1VtRGxvZE1vNkIxV1NJTFdDUC9OVFNtRDV0MlZPMnF4T3kzMTQzd1dRdm1TejBFL2dLWHVDMWsvL1hacXZNeDdnRHJPcERFbVcyUVB6Z2lhcmkxUTlXZU5KdldWTFc1OVp3SkIvOGhPR3RRd2g5NEJtODZSUThHVWpwZ0J1SWVyRlQzdEo2YkdOS0NNc0VoT2U1VDBBU0JvY205UWJmS3pGTTFTWkNDbWcrSmhHeUY3eFlISC9PUThubTRwcWs1S1haVXk2WGcvd3hkZ1YxeldEUUhZUWszWkZ6MXlRb2ZkMXdjMTB2dE5WekxwQVFVQVV3OHVKdjdHME5KdGFGR0VuMTJxYTVRaVhQeEluUWhZZmxvekZjNXpWY1N1VEE3Q0hEK1RqTTB4VUtuOUcrY3hLanNmVVZqU0I5TFppc2FSSEEvcHdwdThKL0djMkwrbjRpVUttVTlDKy94VUsvSElJYlhkQTVsS21KQzZxTlV0VktUWGt4UExDN2NabU1kK0VlZGZOdWtqdlZDT2NXd1pXZFpkbzNtMW1xSlY3U2d3bzA1WjZmMWRXa1lPU0ZKM21ScVlmdmFaOXZlVHFWWm16NlVPZU9pN1lPSWV2WGQ0WDlOTHBpT0V2WHFFRU05VUk5cHhTZlByQ2xjaXdVNzRaNGZHOHpsNUZ6dTMrVUFjUTl5U0lEaU9kQW83SEdaRzgrMklWYktlSlJWTmM2dk1FK0VFVkxJQ0JzZDlDdzNib082b3NMWCttTENQM0l1L3NVV1owNE96Q2dMWGliWUNGZ29hbUk3aTZXQWVya3VtMDJoa1A2SmoxYjcyRFgxMEFtV21CRGg0NlZPOU1pSDl2Yk81cGd6RHRZbFpNQVBTSjVSWUt5SVdxalhtSmx5SlhKcXpzdTdHcUcySnpoWmd0Y3FCM21xLzhFdTBVaFpJRXJIZVBjNXVtYUd6OENzd1RiSldMVGVQcDU2RUVILzZEd2ZSYlBzNUszQS96MjNHKyt5MkFBOVpLdnI4YU5qN2h0ZnY5c3I4cFNkdi9rYjFqZXVmbHhRZEFlM3NWcFpwdy9NeFYzb2wweHBCcW9TNDZlM0VKdDJQeDlPVHRaSmc3QzBsc3B3am5jT25wOVhsWk1JY2t4TGtSMDVUdXQxWHhMb2ZvenVYUkVBMHFUMU1MVzBwdjl1UThSMlVPMXl2UkJIV0RLVnVqSjFpVXFNVmF3azFrYlBOWjZiazg0WDM0K3FMQ0FxcmNub2RMQmUzdjVzbzh4YVhFVE1LWXZ5UmxoOG8vZUJiYlJJNU9MbDlKcVNWZEk5d1BwSEJGd0x1STFjWmdoTGtDNXZiZlRTbmVKMjBWbEpobGFTTzJHZU00NXY5VUJmL0NyRzVRTFJhVXZVZEdiL3oyYUw5aWtoNEFXVzQxZ09zRDBqQWo3em5jWHM5Ti9NUXVBN2RQM0JHaUtONExrVHBwYytGb1A0aElRSjRXUjd2am5vSkhxZU9HTzZyQUNadmRBSnh6eXVGOERyMkVMR3ZMcWNyVWJFNG03dW4yb0k2TkNOaG15V1NGZ016enhEbFhiYkUydW1FZ0drQTdaVFR6WWd4RnBqbGZCWjkzRnZwVEJXVnlGT0hsUk9mLzJtTXNiYUR4VThVTURxZXF5MTRVQ1F1ZHhadmVBMU9SdjV2eVMwWkk1ZTRTeGsxd29WRlY3Vi9WSzFZS21VeFpUMWFab2JKTWlmMDFkZTg5bko2NEIwdUY1ZG1Yam4xeWVTeU80TE1RMWVVbldIaU12WkRpdHZISm9YRVZhazZaYlBDUTlsclFReDk1QW1xMWQ3a0hCT1IybHdxSTZqL2E1SThTMnRqc01Ock05QXlmNWtiQmV0RVBNVW9mazF1eTAxVlZPTlZ1WTJVQTQ2OWZHQmY4VmsvWElValYvZUdjbHpXeVdaR3diOEhhUzBwajJnakMrdS9jU1dHbGhvUmMrdnA3bTl5U2R1bFc2R2I0Q3gyMEN0b0FDS2hXak54bXl2dERrMXlIYW53T1FtcTh4cmpqK1BBK29HOUNOSlROSVJhSkJweWRVR2ZkbTdiZ21pVzQvVHMrQ1gxMk52R1FLeWpRTEhYN28wNktZTDdZZUxqZ2FWdXFkelBuWURJZXJpcUxVQkZHdjI3bVhLZHpNTSszTHJQQ2tGSHpMVzl0OFpaNGRuRExpdzNMbHdpZ3NUbGV0ZlV3YlNZQXpqT0pNV0dhT09FTGNzeGJpVDdsV3RmTXBkQmFUUVA5UWtydVRRSzREamFvcHZsczRWUFNaTW9tM0d2bkNjZVhYUXNYWVRucmRSSG43YnVkeGYzSFIwZUxWV253NTNhMGg1Qk45SWRaYjREZkpuQnY2d000a0NMRzJZRWpvU1lSLzMzOFRFT3hQY2lFL2pETGIyd2psSXdFSjJoaEZvY1VVN040RU5CYzh5YWw4Vndqb2wrSUUwYkMyQjU3aTRnMDZUK0hVT2tncmRFRStzak1sVnJEdm1udjFZSWFUV3VnaWxTQzk0Y2poWXMzZ2FiQjJPSnRxOFVDZktnQm9iUXlqTU9TYnJ4OHo5MlVtZ2hpQkZxSkxEM21YODV4S1ZSM0pTcmRyRTN0VS9iSGJOYlZsdlNkNDZmOXlGSGcyTFVQUFcwOVNGalFMR0FkT1M3VVBSbTdPeHU5V1hrSVdRb2s0b3FpZFMrNW9NazRMb0pFemxWNThzME80WDladEdkUTE0RmZkdWsra0VLWVJrY05kR01FUityZTRjb2JnSEpDRnlEUXlyc0s2aEVsb3BlRFBPMXFoUTY4UTMvbGcxR29TSGwxNUszQVBvUXR5NEdlaEpqNEZYY3Y4Nko2SjlnQzBLTXVjZ3NCcG1ITlZFTWpUU0dqV0F4VVY0cmZNVmo0OHhobDZ1OVh0eE9NSVA1Y0hJUk1yUjZZT2VJUVR6OWMyV3lTa1gyNWdyWGFOYTVQdDJwMXhiRzdYMTFwYklnZnNtTUtJWDN2ZTIyNFliOUpzTTlxVkwvd1VsR1g4RWpFR2ZjZWw1NVgyRmJnT3lLc2FySjA5bmIrR0lNSUZndkJVMW5vZHpJMXE4UzFyeWZtV1dvY3ZaOXBBT1FQSkc5dDd5L1lIQnkybFNxdlJKV3FLdEpFYmhCTkJnOGlrVWxZQTIya2FlM1VBVVJHUjV4UE5VSXNxSmF1SUtJcmR4a0pjMGdNOXV0VTcvTVozK0pYSnUxWTlZNXIzQm55d2plSFM3ZEwrU0xOaGZYOWtJQVBLODhWV3llVXpHdk9qN3NGTjdYQU5zcisrVzBsMzVoRGhEVE4wOUFvekp2bS9wZzUxaThjRzhYMDlHT2hQM2k4Z3lnY25LNUZvYW5hc3BRT3FxeGgxNDZtQ0ExZkhDejhZRGNuTzZRZnZ5RmxoQ29HR3VUZ0tPcVVnbUVKdmVTTmZ1TXdyUndHWFhnZXJWRURVell6QkMwM1M4TnZ2bEg0U214bUZlZjBvejFTTy9tVEVvV1BxRXlGRGM2R1JpQndNbWRiN3RJVG5zLzRQajJobVVNeVNpM2trYlJuRTVtcG5iR0xZVk53bE9QeFJBVkhFMlB3bmZ2clczZFNSbCtLUVNkdFE3bTNMNHA4MG9JelVOaDlmZnZlYTZ0Zk5nR09wMlc4M0NTVlZ0UXUxWHAxU2lkNEhVMmZJRFI2bjM3SitxdEdQaDV3VmFrN0hsa0F0VDV1RE1ucXg2QmlQTHdDTXpOSWlIcG1ld3UzaGkrS2JsQytCaFhTbUY4L1B6ZWZybTZoRXptK0tUeXhhRUZGU0JFV00yZGo4RENLZnAvRWtFYjlXcWFVQXRHRFl0UFhMZ1RDZ21QWkl6NXA1NUppNzMvMDc5aUFscnJWSUwvd3ZtTW50NUk4MWgrQUZlR00zcEpQRjM2djM0WEYxUjgvNE42bFpFWVp2d2ZVYjRGSkFYTkhOdG5KQ2VkNXpHOUxhc2NmUDZpNmhBMThMWk9ZenAxc0tpRTZCMjRJNkpXaVB0WXkwenV0eitZL3BXK09TQUdYalk0Z1oxQkkvdnZxenFqTC9YU0JSRHVjdmsyb0FBdDV5L280dVU4dlIybXhWd1NYTnZvbFBVZmdrM29uZVludTVwaDZaOU5KUThRcHdJMmtZNUpxVm5HaTB1WWdieGdsVDl3ODVRSFp5Z3IvL3RERTQ1anF4Ly9tRzg4Zi9ucWFJZk5sdVpDNmN3dHBWK0h2TTd6ZU12Y0ZWaUU1di9rV2tyWVNBU3JCaDhtRDNaUGNXWUJRQURnWExETW10d1ZIRVpnQ2sybXZmdWF1S2VDU2EvZmRRZm53WDdFQkZGMGd0SzlxZDNOZ0RrbmxHQVBUVEVQZUdPWktnZ0J6UXMyTS85Q25xOERORFliUkd3OFNJQnRjOFNtWjFLYkV4eTUxNWtpTk5pQ3NFdk5pYWE4d0c4OWk1ck5tdnhnPT08L3hlbmM6Q2lwaGVyVmFsdWU+CiAgIDwveGVuYzpDaXBoZXJEYXRhPgo8L3hlbmM6RW5jcnlwdGVkRGF0YT48L3NhbWw6RW5jcnlwdGVkQXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file diff --git a/test/samples/encoded/samlresponse_extraspace.txt b/test/samples/encoded/samlresponse_extraspace.txt new file mode 100644 index 0000000..602e96e --- /dev/null +++ b/test/samples/encoded/samlresponse_extraspace.txt @@ -0,0 +1 @@ +PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiB4bWxuczplbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6eDUwMD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb2ZpbGVzOmF0dHJpYnV0ZTpYNTAwIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9hdmlsbGFjaGxhYi5hdXRoMC5jb20vbG9naW4vY2FsbGJhY2s/Y29ubmVjdGlvbj1DSE9QIiBJRD0icGZ4MmJhMzUwMzgtN2ZmZi1mOWMwLWM5YmMtMTQ2MmUxNDU1YTc2IiBJc3N1ZUluc3RhbnQ9IjIwMTYtMDgtMTBUMTk6MjA6MjhaIiBWZXJzaW9uPSIyLjAiPgogIDxzYW1sOklzc3VlciBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSI+aHR0cDovL2NpZG1mZWQuY2hvcC5lZHUvb2FtL2ZlZDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4MmJhMzUwMzgtN2ZmZi1mOWMwLWM5YmMtMTQ2MmUxNDU1YTc2Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+cXc3UU9xY0ZRVnI0VUg4a2RWUDNDYk9MaTQybGdnd1d0a0I5cFUwZGJhND0KPC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPm1hVHFPN2hxeFB5RmN6QjNoV0VkMUhwd1l6QVpFMmZ1cXMzeXE3aGZ3TlVsdyt4Rk9HMHFMTGRXUWRQRTYvakNYMkhqVXRnR2Y4NmV5ZWpYZkdTOGRpcms4UEFydVh5dm9kNTVhRytqUUQ2UXI5SFU4RDNKQ3NOdC9HRHBmTVp4UkFXVEY3L3dUYk1TUng5Rk44UjJSVUZRWUFxWDRrODFsU0xvR0hvSTFVdUo1S2ZVMXV3R2VyZjVSZE5OTERyRktucittdTdSa0dWUGo4Q1AvaVlLUnE5S3hLd0ExOFBicEJIWER1dHh1OWM1TmVkQ0N6UnVORFc2V0ZEanlwMzIrRnV5Wkswc3pLcGQxVUtaNGpDaXJBYnNhemhYTXowcjFrM004dzZZaGE4T2tpQ2E2OHpIeUxwQm9MRnF2YTh3RithdktIYS9USEY5eVNLelpaR3k1QT09PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGEvPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT4KICA8c2FtbHA6U3RhdHVzPgogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPgogIDxzYW1sOkFzc2VydGlvbiBJRD0iaWQtWS1Sd0hpNlJQOGpNVVI4a3IxRlZ6SHVOdmJ1ck9JZUs2d0dwTmpkLSIgSXNzdWVJbnN0YW50PSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgVmVyc2lvbj0iMi4wIj4KICAgIDxzYW1sOklzc3VlciBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSI+aHR0cDovL2NpZG1mZWQuY2hvcC5lZHUvb2FtL2ZlZDwvc2FtbDpJc3N1ZXI+CiAgICA8c2FtbDpTdWJqZWN0PgogICAgICA8c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDp1bnNwZWNpZmllZCI+SGFua2VlSkBlbWFpbC5jaG9wLmVkdTwvc2FtbDpOYW1lSUQ+CiAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4KICAgICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTYtMDgtMTBUMTk6MjU6MjhaIiBSZWNpcGllbnQ9Imh0dHBzOi8vYXZpbGxhY2hsYWIuYXV0aDAuY29tL2xvZ2luL2NhbGxiYWNrP2Nvbm5lY3Rpb249Q0hPUCIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPgogICAgPC9zYW1sOlN1YmplY3Q+CiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNi0wOC0xMFQxOToyMDoyOFoiIE5vdE9uT3JBZnRlcj0iMjAxNi0wOC0xMFQxOToyNToyOFoiPgogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgICAgIDxzYW1sOkF1ZGllbmNlPnVybjphdXRoMDphdmlsbGFjaGxhYjpDSE9QPC9zYW1sOkF1ZGllbmNlPgogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgIDwvc2FtbDpDb25kaXRpb25zPgogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE2LTA4LTEwVDE5OjIwOjI4WiIgU2Vzc2lvbkluZGV4PSJpZC12TVctM3JLLXZSZW9ldU9kNUF0VjhKYi1RUTRDbVEwekc0NWZUWUoxIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDE2LTA4LTEwVDIwOjIwOjI4WiI+CiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj5MREFQU2NoZW1lX0dSSU48L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+CiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+CiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+CiAgPC9zYW1sOkFzc2VydGlvbj4KPC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file diff --git a/test/samples/encoded/samlresponse_signedassertion_dsprefix.txt b/test/samples/encoded/samlresponse_signedassertion_dsprefix.txt new file mode 100644 index 0000000..853fa73 --- /dev/null +++ b/test/samples/encoded/samlresponse_signedassertion_dsprefix.txt @@ -0,0 +1 @@ +PHNhbWxwOlJlc3BvbnNlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0iX2ViMGQ0MzZkNzQyMTAyMmE0ZWZmIiBJblJlc3BvbnNlVG89Il8yTjVHR3Aybm1JVENGYmN5R1NLamFRM2FpNkt4OWNBd0RoQkdYMWdBSnl2Q3JsSnZvRVFkakVnVHNmYWpnTTltN2oudy5JOUZ6MWRkVmpaOWxLWkNoY3NwdHA5a3hrQ3VxY3diZU5lLmxKeVZRcEI4aVNhNGF3RllzajlBNXI3UkViNUpwSEg3MkI2ZmVndUhGRlBFOE1hazN1NGhTRUtsOV84bW9pWExkQTU3V1ZoendhOFhZeG40bURzaFNwM1hiMFBFWktPREhNdHhsVlhheWNHWXVNZ0MyMEdwZkNBIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNi0wMi0wMVQyMjoyNDo1MloiIERlc3RpbmF0aW9uPSJodHRwczovL2F1dGgwLWRldi1lZC5teS5zYWxlc2ZvcmNlLmNvbSI+PHNhbWw6SXNzdWVyIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPnVybjpmaXh0dXJlLXRlc3Q8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBWZXJzaW9uPSIyLjAiIElEPSJfSW40RjVGbmlPNDV5TkxaVTRrTTdibTZOTHRyYVV3c0oiIElzc3VlSW5zdGFudD0iMjAxNi0wMi0wMVQyMjoyNDo1Mi40MzlaIj48c2FtbDpJc3N1ZXI+dXJuOmZpeHR1cmUtdGVzdDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkczpSZWZlcmVuY2UgVVJJPSIjX0luNEY1Rm5pTzQ1eU5MWlU0a003Ym02Tkx0cmFVd3NKIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+TGJjL2NSanIxVjBIUzZaQTVWTzR5cTdUTXZIK0g4eURqdzJjU0lHMU9ZYz08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+ZzlibGRydDlVTks0bU5NbEl2bFhQSzFUa2ZZaStHUXlwTThzTHE1Z2VkQmZSMDU2cW1qUnpLckRzOEhYMHdVMXZKaDN6cnNOcHcwR055aVkvamlhOGpQQkJkc3dqTHBDTTUzNDF5aVRyNXFDNzhvRFk0ZGNZOTZCN1N5UCs3VXI2MVNGbTNOUWtrUDd6QnFUYnZycW9vWHU1RVBNUjdSMHozdEptZ25BZnpDbytvS3h3bEE2cmF0d2xXWldQQmtYRzVmYmM5MEtIZnhaS2ZLOGVWTlRRajJUTmlrYzZaaFFsWEI5aG05ekNqMDNoRjNlL29vV1NTaUNlOUwvM0RmSENpbk1jU2ozMjJLc1dOZmNBVEI4dWVwcncvWll3OUFwYmRld2hUcyt5NFlGdGNzQktpei83ZUlmTmJHcERQTUtVN3lxeEFVR2w2WmJ1TmNON2dlN2h3PT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUVEekNDQXZlZ0F3SUJBZ0lKQUxyOUh3Z3JRN0dlTUEwR0NTcUdTSWIzRFFFQkJRVUFNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaREFlRncweE1qRXlNamt4TlRNd05EZGFGdzB4TXpBeE1qZ3hOVE13TkRkYU1HSXhHREFXQmdOVkJBTVREMkYxZEdnd0xtRjFkR2d3TG1OdmJURVNNQkFHQTFVRUNoTUpRWFYwYURBZ1RFeERNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1YyRnphR2x1WjNSdmJqRVFNQTRHQTFVRUJ4TUhVbVZrYlc5dVpEQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1aaVZtTkhpWExsZHJnYlM1ME9OTk9IN3BKMnpnNk9jU01rWVpHRFpKYk9aL1Rxd2F1QzZKT25JNyt4dGtQSnNRSFpTRkpzNFUwc3JqWkt6RENtYXoyakxBSkRTaFAyamFYbHJraTE2bkRMUEUvL0lHQWczQkpndVNtQkNXcERiU205MlY5aFNzRStNaHg2YkRhSml3OHlRK1E4aVNtMGFUUVp0cDZPNElDTXUwMEVTZGg5TkpxSUVDRUx2UDMxQURWMVhoajdJYnl5VlBERnhNdjNvbDVCeVNFOXd3d09GVXEvd3Y3WHo5TFJpVWpVelBPK0xxM09NM28vdUNEYms3akQ3WHJHVXVPeWRBTEQ4VUxzWHA0RXVETytuRmJlWEIvaUtuZFp5bnVWS29raXJ5d2wybkQySVAwL3luY2RMUVo4QnlJeXFQM0c4MmZxL2w4cDdBc0NBd0VBQWFPQnh6Q0J4REFkQmdOVkhRNEVGZ1FVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBZd2daUUdBMVVkSXdTQmpEQ0JpWUFVSEkyclVYZUJqVHYxekFsbGFQR3JIRmNFSzBhaFpxUmtNR0l4R0RBV0JnTlZCQU1URDJGMWRHZ3dMbUYxZEdnd0xtTnZiVEVTTUJBR0ExVUVDaE1KUVhWMGFEQWdURXhETVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtWMkZ6YUdsdVozUnZiakVRTUE0R0ExVUVCeE1IVW1Wa2JXOXVaSUlKQUxyOUh3Z3JRN0dlTUF3R0ExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRnJYSWhDeTRUNGVHcmlrYjBSMndIdi91UzU0OHIzcFp5QlYwQ0RiY1J3QXRibnBKTXZrR0ZxS1ZwNHBteW9JRFNWTksvaitzTEVzaEIyMFhmdGV6SFp5UkpiQ1VidEt2WFE2RnN4b2VaTWxOMElUWUtUYW9CWktoVXh4ajkwb3RBaE5DNThxd0dVUHF0Mkxld0poSHlMdWNLa0dKMW1RM2I1eEtaNTMyVG91Zm91SDlWTGhpZzNIMUtueFdvL3pNRDZLZThjQ2s2cU85aHR1aEkwNnMzR1FHUzFRV1F0QW1tMTdDNlRmS2dEd1FGWndocUhVVVpud0tSSDhnVTZPZ1pzdmhnVjFCN0g1bWpaY3U1N0tNaURCZWtVOU1FWTBEQ1ZUTjNXa21jVElJNjY4ekxzSnJrTlg2UEVmY2sxQU1CYlZFNnBFVUtjV3dxM3VhTHZsQVVvPTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6dW5zcGVjaWZpZWQiPjEyMzQ1Njc4PC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDE2LTAyLTAxVDIzOjI0OjUyLjQzOVoiIEluUmVzcG9uc2VUbz0iXzJONUdHcDJubUlUQ0ZiY3lHU0tqYVEzYWk2S3g5Y0F3RGhCR1gxZ0FKeXZDcmxKdm9FUWRqRWdUc2ZhamdNOW03ai53Lkk5RnoxZGRWalo5bEtaQ2hjc3B0cDlreGtDdXFjd2JlTmUubEp5VlFwQjhpU2E0YXdGWXNqOUE1cjdSRWI1SnBISDcyQjZmZWd1SEZGUEU4TWFrM3U0aFNFS2w5Xzhtb2lYTGRBNTdXVmh6d2E4WFl4bjRtRHNoU3AzWGIwUEVaS09ESE10eGxWWGF5Y0dZdU1nQzIwR3BmQ0EiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNi0wMi0wMVQyMjoyNDo1Mi40MzlaIiBOb3RPbk9yQWZ0ZXI9IjIwMTYtMDItMDFUMjM6MjQ6NTIuNDM5WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwczovL2F1dGgwLWRldi1lZC5teS5zYWxlc2ZvcmNlLmNvbTwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIj48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj4xMjM0NTY3ODwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5qZm9vQGdtYWlsLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+Sm9obiBGb288L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+Sm9objwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zdXJuYW1lIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6YW55VHlwZSI+Rm9vPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTYtMDItMDFUMjI6MjQ6NTIuNDM5WiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOnVuc3BlY2lmaWVkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file diff --git a/test/samples/encoded/samlresponse_utf8.txt b/test/samples/encoded/samlresponse_utf8.txt new file mode 100644 index 0000000..4355f9e --- /dev/null +++ b/test/samples/encoded/samlresponse_utf8.txt @@ -0,0 +1 @@ +PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfZDVlYTNjMDRmOTE5ZDgyNmM0MjE1NDExNGYzZmExYTU1ZmIxYjhmNWVhIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNy0xMS0xNlQyMzo1NzozMVoiIERlc3RpbmF0aW9uPSJodHRwczovL2xvZ2luMC5teWF1dGgwLmNvbS9sb2dpbi9jYWxsYmFjayIgSW5SZXNwb25zZVRvPSJfMzE3ZTI2YzFiODNiYjNlOGNlZGMiPjxzYW1sOklzc3Vlcj5odHRwOi8vbG9jYWxob3N0OjgwODAvc2ltcGxlc2FtbC9zYW1sMi9pZHAvbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KICA8ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPgogIDxkczpSZWZlcmVuY2UgVVJJPSIjX2Q1ZWEzYzA0ZjkxOWQ4MjZjNDIxNTQxMTRmM2ZhMWE1NWZiMWI4ZjVlYSI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+UVRyWDZqSHpVcTdZSm1yUUhIZlkrc1BIN0lBPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5mVDNNSVpKZ0JNKzJpOHdNclpOQmJlMmZrRUJLYksxb2puUnFnYVB2WkRyV3lQUWNEWS9ia2hhRjk1NG5IK24wWkp1ZDM2YmVxU2xDekVteFQrT0YvTU93RTJvRWdxV2F2Ull2VFJwSXZFckVDdmJIYW83UytYRDQwZm5xRFdveWNEWlJGRGFNWDUvVjVTK1o1Y0RhN1l1b3UxODBPT2RxbHhld05mcTg3ek0rcTA4NWdyaVJsNVR3d3hJU04xTklGYTR0ejdtRGZMaWkzak5pTEI2SDhUUEFiUmhRNXFFQTJSM3BZL1E3L1daM3BQVUNHb1hheUtGWEVuUTBZcVFadzNSZ1BQS1ZwZDd0MmZIMTV0Yy8xcFlscXN2KzNQZlI4VGxXQjZBb3VVYWw3NGpCV0E0QkJaT3pDelNPQUdJMld1K0hhdFNXN0ZUYmJCbnhPdjl5K0E9PTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSURYVENDQWtXZ0F3SUJBZ0lKQUxtVlZ1RFd1NE5ZTUEwR0NTcUdTSWIzRFFFQkN3VUFNRVV4Q3pBSkJnTlZCQVlUQWtGVk1STXdFUVlEVlFRSURBcFRiMjFsTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdIaGNOTVRZeE1qTXhNVFF6TkRRM1doY05ORGd3TmpJMU1UUXpORFEzV2pCRk1Rc3dDUVlEVlFRR0V3SkJWVEVUTUJFR0ExVUVDQXdLVTI5dFpTMVRkR0YwWlRFaE1COEdBMVVFQ2d3WVNXNTBaWEp1WlhRZ1YybGtaMmwwY3lCUWRIa2dUSFJrTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6VUNGb3pnTmIxaDFNMGp6TlJTQ2poT0JuUit1VmJWcGFXZlhZSVIrQWhXRGRFZTVyeVkrQ2dhdk9nOGJmTHlieXpGZGVobFlkRFJna2VkRUIvR2pHOGFKdzA2bDBxRjRqRE9BdzBrRXlnV0N1Mm1jSDdYT3hSdCtZQUgzVFZIYS9IdTFXM1dqemtvYnFxcUxROGdrS1dXTTI3Zk9nQVo2R2llYUpCTjZWQlNNTWNQZXkzSFdMQm1jK1RZSm12MWRiYU8yakhoS2g4cGZLdzBXMTJWTThQMVBJTzhndjRQaHUvdXVKWWllQldLaXhCRXl5MGxIanlpeFlGQ1IxMnhkaDRDQTQ3cTk1OFpSR25uRFVHRlZFMVFoZ1JhY0pDT1o5YmQ1dDltcjhLTGFWQllUQ0pvNUVSRThqeW1hYjVkUHFlNXFLZkpzQ1ppcVdnbGJqVW85dHdJREFRQUJvMUF3VGpBZEJnTlZIUTRFRmdRVXhwdXdjcy9DWVFPeXVpK3IxRyszS3hCTmh4a3dId1lEVlIwakJCZ3dGb0FVeHB1d2NzL0NZUU95dWkrcjFHKzNLeEJOaHhrd0RBWURWUjBUQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBQWlXVUtzLzJ4L3ZpTkNLaTNZNmJsRXVDdEFHaHpPT1o5RWpydko4K0NPSDNSYWczdFZCV3JjQlozL3VoaFBxNWd5OWxxdzRPa3ZFd3M5OS81akZzWDFGSjZNS0JncWZ1eTd5aDVzMVlmTTBBTkhZY3pNbVlwWmVBY1FmMkNHQWFWZndUVGZTbHpOTHNGMmxXL2x5N3lhcEZ6bFlTSkxHb1ZFK09IRXU4ZzVTbE5BQ1VFZmtYdys1RWdoaCtLemxJTjdSNlE3cjJpeFdORkJDL2pXZjdOS1VmSnlYOHFJRzVtZDFZVWVUNkdCVzlCbTIvMS9SaU8yNEpUYVlsZkxkS0s5VFliOHNHNUIrT0xhYjJESW1HOTlDSjI1UmtBY1NvYldORjV6RDBPNmxnT28zY0VkQi9rc0NxM2htdGxDL0RsTFovRDhDSis3VnVablMxclIybmFRPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpFbmNyeXB0ZWRBc3NlcnRpb24+PHhlbmM6RW5jcnlwdGVkRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiIHhtbG5zOmRzaWc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiIFR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI0VsZW1lbnQiPjx4ZW5jOkVuY3J5cHRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNhZXMxMjgtY2JjIi8+PGRzaWc6S2V5SW5mbyB4bWxuczpkc2lnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48eGVuYzpFbmNyeXB0ZWRLZXk+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIi8+PHhlbmM6Q2lwaGVyRGF0YT48eGVuYzpDaXBoZXJWYWx1ZT5yVWU4Qms3cGRDTnY2UjU1aXJwa0VXYkhwSGxFMUJ6aWdsdFZBbk9rZ0UrVnZWOFllRU43c0c5ZXZnWVpFVmJQTEluMGtnbDk2dUlYRFNIUEtja2RpTDJBcTRvNmZncUJhSTVIWEsrZnVKMFJzRUZNWWlZblY4R3RRRkQ0ZXV1RUx5VHIvZERwQUpDYnhKTWd4b2pic3k1RGxMK3RqNGJYV0dRd0daZThZZE1QRkpYS1NiZnhuTExpeHNnSHV1bGRpRlBQWlkxaG54T08vYSt4RnJranp3Vmt0clNaSDNHNWtwRlVoOTh3aTMrS3RLV2FzQUhna3JRRTVkR2lnUHRNMGd4TTlNVjgrcnZBYkxidkxIZGZEQk5CaHdLTnpYSXNpN2N6OUlkeU5hMUR3Vko5UnBDTmVnWmtFOCt4d0N6Z3EycVlmL0pyOEU0YkhMV1BUZjFUcFE9PTwveGVuYzpDaXBoZXJWYWx1ZT48L3hlbmM6Q2lwaGVyRGF0YT48L3hlbmM6RW5jcnlwdGVkS2V5PjwvZHNpZzpLZXlJbmZvPgogICA8eGVuYzpDaXBoZXJEYXRhPgogICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5naVdSQlFUd1c2RHVpWk5TeFBrNUg0c2RkaUFFeGJtM3VVZWZTNmppYS81SHJWd0duSEhtSjUxSFFpaVFFMC9CbXBlaFlMS3djRGhIY21ORGF5b2VjMmdSQktrR1FVWldobjdRNWRmbmt1NDF1a1k4Sll0cnpkd1VZeVpKdVBORTB1OUhabnNhODFwOEJKckc4STg1WmhxOEdBLzlHREREMjZSMTNkQ3ZQa2tCYjJrallpbDQwbXozZ0FQcVRTT2ZqMmlJSEJRYVI0TWJEMldrMXh3WmZvUDgrZ1lJaHA1TGRrTncyNjFkUUhJN0dPNndxdHM0bmUreEpONStOUEdnZ1RaQTIwNzhWY3o3OGNiMEFyVjIzdUlYa1RoNk5HK2NmakRHWFh3SnVvU2syUWdIamgzNWRUdVJBa1RSVVFNcC91UE5URW5NWFJxbDNTdGk0UWltZWpuRk5EN0xWTy9DSmZKcXZHZUpYNFdLaXFDb0FVMmMwSmhOOXVFaWt3dktiRG5qVjVQcFJIQTlJckhxUXdseUx0L2xuKzFkTnBXQ3ErTlFrUllySFFHYTRJSUhGQmdITXBNNkFiN0p4MFliQXg5andDN0E5SVpGbmFFanhrY0h1T21CZXBwdk5ManhsNzFPbFU2b3BURzRCTXplV2hXeWhZN0RRVUJCUFErVis2K3ZvZGYzT0dmQ2gvMTdscVQ3bnpoQ0lRZjJiVVQ0R2c0Y2hEVEFtRkJZY1dNVERUanNFdy9PWUdtSnJva0lSUFduME8rbEdiaU9lSFpROWpBMXNrWjNVT1IwRllXR2JNRkYweWwrRHZuRFBwN3cyc2FVbXQ5aE1IRzlmM0hvRUVINzcwUEhqelhLSDcvODJ4OWVtSVd6VTlpOGNxaFM4dFR1NHROWktoMGo2dVVtcU1IV1VqS2pWUURrbEVJQnJCY1ZsbHpyY3pCMnFRSHlodnp6cnk2cEFSeHl6RGRldVhUaWFqN0w4NjdtNTQ1QjJuUi8welZxcWdLcExPaW0zY05MUlJ1Y3RYSGJFSzNFbjJKdis2YzFVY1JZKzZQQmdOeG5idENzbm5NODRnT3FidkR6TVZGaHFsd3kvS2toMHFib3BVcnFFSE9ET2ROY1g0TU1PNnY5N3hsQm94M3FJZkFuTVhhejFmckcyZzJBVFdZWG9Ma25zNkRsbXBTQkprZDFYclRDY2VyMDdnMFRhM3k4T1o5ZXdWdGVHYkgzcG9ORmhRNk1zaTBkb0N3VmJMUnNnZFBldmtVa3RYZmxrQ2NqNWZ1N3B1Z0JuK2thM09vc3dVcGl6U05EalB6dTJLYmJ0M0ErK1RmVlVDUEFxNllMSm9aRmVvQ0FWRXZlM3VCZHhSbzE1cWM0Rk9obGFPSXhZcFRyWXRIeURSMVhWN0pvYXlCbFU5a0lHWjFGY0pITWNROEhmallwRktmYTFZTkJtWS83NytCbVloMmZPMXF1S0ZoMm9OQ3Y5ZG1CN0ZSY2hOcDh0Q1dYcHVZdVROdTZCalNFSlM1M21qSEZld005d3p5OGNmeTRoTzY0UHc1SmhoNWJ2T09NRnhFaEFZR2x2Sm93UzkwMm1IVVl1T1ZVYUVxSUJDRUw1N1VFN0MyZGJ1VDJaRW1TSW1YMVJNaTNBNkhRVjJSaXNENDRHM2VwM0E2WWxWa3JwalVrUWlkLy81byt6bWZ1K3VJTzM2TDhUbXk0M2pmcE54bGZzamFnMHoyTVorYldHeElvbVV5cmxJN29WOW94U1lLN242VHMzendVQ01kWXNMTFNJSndmSnVuSHZObjR4TVJUcjZKOXd4WW1DRTdLYzQwRm1ZK21QM1dxM1c4UlNtRDQ4M0Irc2lqSnFHQTNBQ3AxSkkzcS9uQ0l2YnZ5SEFzSUF1MHNuaXQvejV6R0RyMm1lUk5tSWZXb2VhRHQxYzRMc29SenJoQWdjWjVXWjNORGExeG0rSjZPUU1VajJGUk5UNWpWclprazZZdnNzV3lxbGhKYTl6Ukc0QmJZUzZscWpMN1VpQUl5REJhd3gwNXlEaU5tRE9adDd4RGI4cmF4K1owVzZ3a3Fyd2loMDV0dUZUc1ZpZ0IzWDJOSWF6NnRDWXN0UTBTbHc3NWFvMko4ZExCNkdRRzlTbU1JcGwyU0FxOHk5SmNvWEJYTEJCcm50OWJ3Z3YrZGlDRWVsUGprcVhERmhkL0JMQzhJVFUzUjZjSGo1MGtHdDFOOXlyNmVVTjNFTXNkUE5xOG5QcFMxa0Q0dHJZS1FuUUpKUWZGVm1VNU9lU0JoNm00eXZNazUrUUNLVVhRUXpzclJOQmdOQ1BPeHNwakZ5RU44QmpseEtrU00zVXZaNThtSlVBV2lNZHJuL3BMZHpwM3p6bjc3S1E0cVJuT2lUR0k4SXlFOUMvblNEeUd4R1I3YkNFanpHVTIyc0p0WUFZemJjUEt5WWhnMUJydGpOc3FDTzR4MEk0WkZqQVJDNGMxaVIxQU54T3YzRHlGT0sxR1huMVRmam9vbWYzUTVhTDk2Q2FUeTQvdkF2YS9hdmhSUzd2UUZwUFhkWmJxcVRmWE5ScFZ1ZWlMNzdidk56SHdRdjhoYzdvckZVVzNneWRGemkyU0VBTGdHUk1aQlJKRTN2Z3Z5cENhNFJyRWhnckxlZlZSN0l2MFNTbExodnZONFhTVWNQeXFFK0IyY3E4RVBCTlc3Z1ViZzh6em5XcFJaSm1YYWk1MDFWc28xQkdEZCtoUER6aGVka1YwTlMyT1lKYkdqTS9uL09oUUl3Q05OUXpXRkpKekppS2dxYmdQU0RvTEYyZkFTbXdCTW1rQXZ2dC9NaU1pV3ZqOUluZyttZ2ZQOEREZWppTFdLK1hKcTlVNExiUmZsd3kvY1o1R1VZV1I1RjFBaHRYU2J2em5ibTY2T1ZNSHV2aUcwRmg0MUxpc0ExRHJWMm0yR2hST0tUeHgyL1lCanhaMjNqTVhQMUthWVhMdzNINGxxU2dXQ3R5SUVRSVduNEp5QWYzRG1lMXBqUy84S3Izd0RZRUV4M1NkdndXZGQwMlZHcGpLbWgxMmk5KzBuTmd4SHVuSDZ3VlkrcWR2N01IUWFpNjNFL0Vsd3htNmVFR2x6MmJwRWhFQy9UOXFXdkV0a2lIVjhqRzlHNDhpVWRsdnBmUStqR1R1N3NqUUFrUXEwdVhKTjQwaEhhdXRjb28reGsxSFBqYjFyWVBIQWloNE9BeFRuZDhWTWZCSGU0cTNwT3g0alhXek9MNWwzSkN1RFFaR1ZReWZ2ajcreUF0MFJQSTdycWNaOC8wcUd6VTFMUGVmUnYzL3RVYU9rVXNJZm5zVitmVXU2WSttVWdNMDNNSVJYblo4OEpWUklVemtHZWI3WDZUbC9xRFZDbHFTei9CTC92NWJaR0pIUURLMEVZT0FSNDJzUG5zUTF6MGFTOUdURENlQ09aa0JqOVRjenIwTWwrZWFYZzJyTUtISytmZ2ZDZ0JOOWdJdXdhQW8yYjN2ay8yaityc2JiU1BORytaOFJlVjFlOFJmaEtwRW5saExOdzdlNVdGSE4rL0NpWHMxY0IxSjdJV2lnT1RXR2FMenBnR05BYlAwb3d0aXJYNktJNkp5NzJrY24yY2FvOU5WV1pMTU1CcWViUk1nQmVXMmwxNE5TNDNzb3d2aERveEoyMFBRNGZXVWo5SklleDh5NGJhVjJyUGlLTndyT0NKUWh2VjVLbEFjUzdBcFhOTHNqZUVWVHErNjBXaTdKWEpRVFM4bGZwTjlrSnpGYk5PVHBVRytwdDFxelJKNnFadEhzNEo3ZlJtVVVXUVJMcUlrY0cxQlVoenNWMk9WMWtrbm1sTXJWdjZQelo5a1BmMGxIOEVaSzJMM2h2QmpKSDhYZndxa3FjYkFpVk93QTdXRUM2b21kMklpbHcrMnFhcHlyd2svd0VKUmd1RGQ5VzlodG5LemVjWEhDVEFqc0Z3RVlLUXl1U0hIKzhKZkJjUnFmdnZ4TkQ3d1lYWHdYVVI2dVFOa2U4MHFiakdYZ3hPRGtMcU9sazNOVVYzZGVFYStZOXYxelkybksxVnd1NDZxT1hGZE9pRThOTXBuRHc1ZWFjM29XS3krWWRCUTQwTUY1ZW5MTEVtQlhUcUtpbWFmYTZPckdPeXphYXB1MVRqcmlNbFUzWWhoNEswSEExT0d5am5OdWZoaE02NCtMNVlNaHNkNTBKNkpqNEQ5YWc0TEVJalRqMmZRWDlnN0llbG1vY3BwaHcxNGc1eGk3VkdseUdNZnRqYmdSVHQ4RHVrZ2dSdW9MeEpCYkRHVU82TWRmQW1hY3FDM2VUQUlDNmtMYjhRTGlVQmd2NGdTSDB5SGxQTGxlTDJwU21mQ3pqTlJjNmsvK3R3V0xINjFnQnY1MGxzT0V3OGVCRjBzMkE0dnlEOHJBTmVZNmNad3F0QTdiSk91SUxRcTBaNW40c21odll4TmtjR1FwY2RiVVM2R0p3TlIrRVNURjdYYS90TmxWSEQvWTVyK1RwVjBFWTdDYnZTUEw0Nmt5eGVGR3lWdS9XU1NaZEErMnB5WCt4V1lEbmxiOXNyVUhtYzJRTmxYQlJYM0x5RUNDdVRDOUdGV2VHWnc0Mm8xZzFNUUJ3TXc1NmR4ZXdWTHlmTXFwa0F1QThUKzBvQXZEQWYxM0pia2VNdnduVlZqcld5RWloQ1Nmcm5FRERxanVMcXRHWENmSVo2eHJqL05LK1pMTkhQNlB5VmJpZzR4WFhJOTVhSVNYdHNXUTdvSDJuallVczQvV0JzejE3Y1NGd29Dek00WHA5NXJqQWJTamQ3NEtxZXhuSXZOV2w2WEVPNC9DOGxXSEFPbW9EN3IzK1lSSXhpZDRidGxjTC8waGhDZnEvWHFIMDYzWGFyTUhCaHNNd01zcFM2Qng0VHY1ZHp2SEZiaTlSZ1A2R0xlb21VaUlWYmQyVlZvYitQTUI1TGl2Q1hQaTNXMlIycXllZUk3R0pxQUxzYmVoMkFHS090Wjc1RDRxN1k5OThRUFFEK1ZaanZ1cnhaNmFHMXdHYk9wc3NBbFFQSHMrakxjUkZmZ1JPVXRaVVEyWk5sOGN1akJ6OU02dW9wTE84MkRha3VLWTN6VkxjWld1cW5XTDJrRURIbk83M0NXYmhtYmx6Tk9XaTNZaCtia1J1OWlCYWw4bVUyejFob3QreEpZVWxkbDRCeHhKQWkyUEVCNGNTVm5kcm5iQ2lWTFQ0L1JBbll5MFNwK3ljZ1RUMVVpY01rRXllZGI1aC9keEpmTlJrcUZXZ3dwdVNGMVd2ZTJMeko1WlJPZ3ZNazFpcXpNdWxaeHl4dDlDSVdZS1FXY1NUTGpLWFRkVUNYb1ZSNlMxTmU1NGpxcjJBYWYxMFhodFppcU13dGJSSndhbXFkOFE4cmNyZkxHUzVKMHc1V0NXVklWV0xuM0hwUDRqbE5ubTg3S3paYW5sK055UUswNUg0NjZHR2lVK1ZJMERHVlhROW1zK0I0cVIwREkvTUJBWEttVjhlSjdSRGZLOXFwMmlRU2crQUh2U3VDSzBGekVTandodUtMK2hLMkc2V2l4cUxHeXk2dzUvSkFweDhlWTJDQ0l5UW9WdTl5anlNSGt5OFNOTUF4SmhzSDBDSFlqMjdOSnJnZlJiZXRFQk9iSjZRc0FCQnNld1RsdWpIZTM0UHhISHpQWFpmQTVDck9TRkZyZnVKcnhlRmNxc0JVWnVBRHZhT2xGSVZidklkQjFGSjYyU0xSNXdJWnlrcWRqc0hZR2NIVHdvdUpudG1tOTUrWGxXeVc0ZXVKZWJ4ZXJXT3kxRE1pa0taTWQ3b3BYZzYrSU1QWjF5SzdCODNoVWNtZ2pSa091OVV2TjB0QkF0ZVhlVVFTYkoxUGxOYmhGcVRkZXVNelAwV25kVGE0VForME1xN0hxaUdnV2dUdGdDY3ROcWdZWEtOUnJwZDhobGtJeGJHL0JMNGE2OE1zRkZHKzFxUDRVZ24vaVhJUTJBTi9OY0h1V05NOTBLK2FjMk5xdnRsWkRTSG1wUjlwMFd2YnRWcUpVYngxRVk4Nytjano4eDgxUGZDUmNTT3ZkTEdJR2JpdmJUejBlK3FPNllBb214VVgwckhqK3BhZmxEQUhmelZIZTJCeDZtRUR1WFVrSVhxNFNiVXN2c0pBNEh0YkpuNDMwNEFTNTJpYkIrRjc1eTFpUWJCc2dEbWVnNy9WVlQ2UlM1bFUxNTlkaDczeWdiSnJBTVZsb0ZsK0ZWdEFiV0ZXZU5EUmlUZDN5dVVqV3Z5Y1FZOXZqbW9vRWVTK0RmaTJJRFhGcVI5N0oyZW0xSHd1bkhqL0ZRN0lucnFNaWJHd1JDUFUyZ3JHTm10Q1BoT0lnSVRZSUZ5RGRGVWY2d3h0KzVFOVNEOFhCZ3dWZ29QRWxjQUVzaXBQN0M0TmRXRFlLa1BXSnVrWGJJSG1Yd3VIZndyTkN5UDN2UTRyN0x0cXV1amwxaWV3SDJRSDRRQmJOUzZvanpQcWdodUx5M29FK3Erd1lDS2UzbzFGZzAxN25UVkxvQUQxL05rRDY0RDJlQjRsYW4vQVNVT3BrUzNSUnpVQWZmakZpSnVpK05WVVVGTVZZN0ZNNEtXZnNMTkVMQmFtTHRmOWlVQzI3aFNtZjlzRmtFcFpWelEwSnNpRGIvUkRHbldzS09idnJGS3hqZFAvQmxqcTMwa3ZuVGsrOXdZUEdSZWZpZHRNb0lNY1VXRG1ERXBIQTBiUGk4eFJ6ZktlWnVSMjdmTkZYVDg0eThjUDB3bVhITC9BTFRVdUM4dEdzMUlGVFQyM0h1N0t0WnduU3ZwM1JEOEJyRGNCZDR2T2NWcjFhODdJeUw1WnhJNlZMcWwvQXJEQXlXQnlqdVozVU1Lckp4MVNrNkpoVys0a3J5RkZuSEg5TWtmcEErTUVnOHVJSHAvQkJpME0reW1jamR2T21BcWFZSE1XMUpISXVkdGRjOGtzb1dDT0cyQUdkZGViQ0hiL21ScWJVZUR4T3RWY0U0b3B2bjZwKzF4c3BFNEduRUdUUkpncVZKMVJNUFFuRTh4WU1mdXdzWG9vTGgxVXZPcXVidW5lcDVxOUt5RDZkc2I3NTVFL1I2U3d5YlJsUlFjZlFWclM4VnhRSXlJaXR1WDY0dVVENTF5Z2pxUkpGT1JUTlRoTElHQmU2RENvT2MxQU9RWjAwMjl6bllCMUhub1RpaEtEMjF5djFRV21HV055c0JJMnFFc2ZGMFNBaDJTNlVxU2N3d21WUDNHVTlDeDRQSWQ4WWNDRnQ5aDJmWFlydldSdEVqMjNyWlNaVUdVQ3RSMU13Mkg0TDJoK1J2S25TZjFBVkdhL3cyNm5hdllFK3VVbGUwajh6Q1VhK2R1UjJWME9yT3NTMlArYmNxNXpCSE5DcEloZk1rYytNNkNrLzljblp2UkJvVmtGV3ZsSUUzU1ZZTStENFVub3NWQTBITDZKK1dQcmJEVXJCTWpJbnY2MDRYaHR3elRKblo1dHc3Rit1amJKUk9jTmlrWEFmc3JneGRadnFib1ZjaXFhR3lYNEQzSXRLVFFFeEh3bFhzNHAwWDloVkJ0WUhsZmQ4cXRvSFJnY3czdzFLekNjK0x0TTVKNkZSOFI4SmJYUkprNFBoeURIVWsxbFJ0TFNST2Y1ZkdrdWZrbTFUekp2UXRkSzAvMDcwSnlVQ0g5K1prVUhydXlxcTJ4Zmlyd2Y1TGNWTzZYeVR5K0pGeUd5ZjAyVC9LTkxpdVB6UTVJb2V4czlGOVV1M05oMHpuZ0JIckJqMllCUU5mc0d4MnVKclIra2YxTFA2Smt2ZzEwT3BJU29MZW52RTQ2NlJyUmR1WGRsZmk4Uk9Fc0crYmthMWlydU9mdTBtL3U0ZWtpYmdWRkZVUHI5dlk0TXdwandoczM2VlVpSmJuTVhyT3FiOUM3NDBSLzVkc0pxbGVGRTd5YzNlQldEdER3aGZVbGJGYXorcVVMYkEzRGxnUnQ1SmU5N3k0WHg4a3o1WGZ1dGFOcFVRZ2F6VVpVaDlFa2tHV1hBMk0vZXlYUExjajdBR3BTNk9nR1cxMEQzdnZvdk9QYjVQVUpIK1VDWjRWWVpUK1VqemN5WGY0SWtLa0pUSlhsQnU1UGp0Y09aQSs1VEF2OWcrT01oa2hyZkhjMTFkTTdWKzR1dk9mK053OWcyVy83ZXdqMi9jb3l5ZmVQUlR5UjhwQ2JxcXdLYitObk54UXYwVXNzTndXcmFKYmU1YTBYMllXeGlWRWwybnNrL3U2d3ZoYzQ3S2JTSUNDajV0VVZsT2ppSytjM3YyL1MvUFgzQkExY2RhcjhxdlpOYm1SRXlld0l4ME84SGRpempiMkVPK3hLNEJsL1VSdnE4cTF4elpXQk9pWGFlNmlTMEVaNGtPOW5TSVBYUkdUSUhGYWZhYm5HbUZ6enJJeUxVamdYa1hNK29MM0lEYTlnYVNoWDRPSkNpbWFNTkJ2c1FoYTVMTGlnTnNwNitWRW9rZUdybUpuc3ZvMk9EaTJjMTZ6bWpaK0NwQTYvTTlreS84c3RwcTJSRi9ZZXpCcWZqaVkycUVxVFh6S1NLRUVyUkVzNWIwcVJVdkg4bCttTzh5U2xJc0J6dGoxMnl0cURTZkF4MkM2Ly9jRUdQNiszMVVsM0s2WUhNbXhMOUFUVXdYR2dBK2pzemsya2dnRW9BSkd1T1RQd3FaZGIyRDQwMTdROWlHbTgwdi9oUWxOZTJlL21PSEF1eEZYcGx4cUVERnpBd0syUlhzSWcyZTgvak1uMldUaFZYMU9qeWRpeVB0NzNvRWM0eWQxQW5icXRWclNTSTdhOENvckU2dThpVzU3N29va1pJMG8zcy9Nb1lmaFpPb0NUbmJVYmlhVEUvbGNlT1QwRElLb1FDa3Z6UWdzZll4cE5WRkRMeDQzREZQaGU0cXJ3MURsU1EyeTZZRkpvdlhlZG5ScTE3NU0vL0t4b3dUM0YyRDFWMXBzUXhGWElUeC82ZjBxSXMyOE4yQm9Ja2Yvd0Q3Wjd0aE9WMEV2Y3VCOFRvWDVyMUNlRUpZY3FrVnlBcTFqNVpVVmNXTkpMMmljSkdCcXVySTlMeTloM0xtYnE3dkRVRVBNTVI2SXFUY3AyMFlXM2pGMytnc0VLak9LUHJ2ZTliQ2Y5NFFlbEFWTlkrM3JYSjNob2xPWndIL1FvTTNMV3I0ZktKdm5QMGxtM1UwRXB1cE1EZWpZQXdaUDEyVHdnSnM4WnFhdTA1Yy9KVFJrWDF1T0FTMENiZWFaVW9aUFJ5RGxlV1d1eHhHQVFxTU00eEFKRHZaWUtGMU5nRTEvMUtQaUprK2lLN0FsU041ZlREc3ZxeFJGVlhoUlRxckxVT0QvVjRYUW9kV01oaFBWdGtDQmFSREMzZ3JmR1JaZHR5NEVodHczYVZwemd5ZHJSRkNWbVZVOGIzZzd0dU56VEtkRGRqOXNHV1lxeDZlR3UxTTVaeUFiMGpSNndmVDZVVkJjSjNiU01uYW1UbFZWWmpZdzNBV01SSmJCblVSdUxOZEpvaXJ1R240d3liSUY5L1g2eWlqUUJIUDFXQjRZUlVyUE5sOUV1eThXeUdvUzdET3FFMFZ6RnZuQkdjaTgrem5wbGJMaVRaTDI1NC9wY2NqMDRGVWhMVzF4RzYrMnhYWmcyeWJYK09ER1JySHJ5NG91YjhSSlhieitIbzVHZjBvUS9zVWs2c1FoSzFtODd6d2pac1RDbVhNPTwveGVuYzpDaXBoZXJWYWx1ZT4KICAgPC94ZW5jOkNpcGhlckRhdGE+CjwveGVuYzpFbmNyeXB0ZWREYXRhPjwvc2FtbDpFbmNyeXB0ZWRBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file diff --git a/test/samples/plain/samlresponse_explicit_iso.txt b/test/samples/plain/samlresponse_explicit_iso.txt new file mode 100644 index 0000000..09e2ab4 --- /dev/null +++ b/test/samples/plain/samlresponse_explicit_iso.txt @@ -0,0 +1,33 @@ + + + BECIZ08ceW8xhfDzWCPqbMK6/Xatmu7hqttsAYzssajZDM=hqHFM7CuuV0PgFxtL2a8AxPsFKaN59LMxb85YmDMpBDPZKoMSwGiwjJorvi4ICBYfGa33UVQIZQK5zkqzWMdR0mkiX5BCC4qYmWQFhSQmvqunti9K+TQ2EIwTouTU4UVxe9jf0K4t8EWsf/9FSCGnJnmdn0WBZR0WDU6Z1XHWZk/jGXr7fI+iCwdm/c6e4Jf6yf/uF80/gDz0GwkHoN9zD6oy931YA432g/WUaHrr9g0OvBRv8UghODYPmHGyDcE9A4DnvtZqJ3LAGwCnCopeKAfScZuCaycAqY06vinlBUxTxMftCDR0AXoSJLt5CSRHYqGkeA/g4JGOAQSp+SdcA== + + + + + Issuer + + nameid + + + + + + + audience + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:Password + + + + + + test@exåmple.com + + + + + \ No newline at end of file diff --git a/test/samples/plain/samlresponse_iso.txt b/test/samples/plain/samlresponse_iso.txt new file mode 100644 index 0000000..bb732d7 --- /dev/null +++ b/test/samples/plain/samlresponse_iso.txt @@ -0,0 +1,32 @@ + + BECIZ08ceW8xhfDzWCPqbMK6/Xatmu7hqttsAYzssajZDM=hqHFM7CuuV0PgFxtL2a8AxPsFKaN59LMxb85YmDMpBDPZKoMSwGiwjJorvi4ICBYfGa33UVQIZQK5zkqzWMdR0mkiX5BCC4qYmWQFhSQmvqunti9K+TQ2EIwTouTU4UVxe9jf0K4t8EWsf/9FSCGnJnmdn0WBZR0WDU6Z1XHWZk/jGXr7fI+iCwdm/c6e4Jf6yf/uF80/gDz0GwkHoN9zD6oy931YA432g/WUaHrr9g0OvBRv8UghODYPmHGyDcE9A4DnvtZqJ3LAGwCnCopeKAfScZuCaycAqY06vinlBUxTxMftCDR0AXoSJLt5CSRHYqGkeA/g4JGOAQSp+SdcA== + + + + + Issuer + + nameid + + + + + + + audience + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:Password + + + + + + test@exåmple.com + + + + + \ No newline at end of file diff --git a/test/samples/plain/samlresponse_saml11_invalid_cert.txt b/test/samples/plain/samlresponse_saml11_invalid_cert.txt new file mode 100644 index 0000000..2af3ecc --- /dev/null +++ b/test/samples/plain/samlresponse_saml11_invalid_cert.txt @@ -0,0 +1 @@ +urn:myappfoourn:oasis:names:tc:SAML:1.0:cm:bearerfoo@bar.comFoo Barfoourn:oasis:names:tc:SAML:1.0:cm:bearerIuz765mYXRSf94HXVMqSQuUAdAiqg5rYmnKoqYf9YQQ=aCKTEa8SIiPNupw7/Th0vMCdAF9nbuVn1Leji+AjF/HcfBNhx6IyIPJmOWhzvU59Wwm2BtaGR7ccrbfeaEzqk5Jd+Jd48/xj9opm5THEaP6FdWrNptVYtdBVto30dfhdSo3if27GVzIJH9+sbhSf5KaZQ3kYnO9grubp+nH3D4PdU5NFtwidMZtj9kSLVPCnCZhr/OReLcD+fXELfi0CsiNOXj2qqaggDfq59phdPYyFtI4UmNduQFoxuM76s0aEF19VjVfGhRa2OgbCLvTeZjeO2SeYlX6xJstk9kkfheViGpdebxfdET+L0Tc6LYRvgbBsYwF6DxE7ZAQvhi4SaQ==THIS-IS-INVALIDuaLvlAUo= diff --git a/test/samples/plain/samlresponse_saml20_invalid_cert.txt b/test/samples/plain/samlresponse_saml20_invalid_cert.txt new file mode 100644 index 0000000..0ad8e17 --- /dev/null +++ b/test/samples/plain/samlresponse_saml20_invalid_cert.txt @@ -0,0 +1 @@ +urn:issuerp7iHnIt5xJZNimGNxh4d9R2J7DML8WNrMwMxmZ1WwSU=pb1Wp/LFbigEj+TNm7gkAwlfIc17LNwUXVTgM8RQnMvYJfIPZbl1yo5xMCh6ObMFwCs1T+gKI5C7jMloX2QhWD/XUffBKiDfkZUg7NI/Jyt5m+Bdst12SNhHBVsNilL9ZCuf+QtQD7301gUhVHP6Ramf4y+XNod9AfzhFLYNfl6fhf/5KA/KkjiOwYW5Ps/43OMXXSeVaeQ7JRU8XqyKbwlB+YXGseFLnyZopv8Cw9Bb2935ADLX111oFBkiRhnMUJW0LMbSWM6UVJ4V0qoW9h+f3isN5+R87RECNeAQP3WSBiddnEuSdhgQYQVnb6s0mThpvs7uvIOlog0FqeSrvQ==THIS-IS-INVALID-xKZ532ToufouH9VLhig3H1KnxWo/=foourn:myappfoo@bar.comFoo Barurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified diff --git a/test/soap-fault-no-info.xml b/test/soap-fault-no-info.xml new file mode 100644 index 0000000..efccabf --- /dev/null +++ b/test/soap-fault-no-info.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/test/soap-fault.xml b/test/soap-fault.xml new file mode 100644 index 0000000..e9780da --- /dev/null +++ b/test/soap-fault.xml @@ -0,0 +1,16 @@ + + + + + env:Sender + + fed:BadRequest + + + + User cancelled + + USER_CANCEL + + + \ No newline at end of file diff --git a/test/state/samlp.state.custom.tests.js b/test/state/samlp.state.custom.tests.js new file mode 100644 index 0000000..428405a --- /dev/null +++ b/test/state/samlp.state.custom.tests.js @@ -0,0 +1,363 @@ +var chai = require('chai'); +var expect = require('chai').expect; +var passport = require('chai-passport-strategy'); +var Strategy = require('../../lib/passport-wsfed-saml2').Strategy; + +chai.use(passport); + +describe('samlp - using custom session state store', function() { + + var SAMLResponse = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfOGU4ZGM1ZjY5YTk4Y2M0YzFmZjM0MjdlNWNlMzQ2MDZmZDY3MmY5MWU2IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1Ij4NCiAgPHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj4NCiAgPHNhbWxwOlN0YXR1cz4NCiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+DQogIDwvc2FtbHA6U3RhdHVzPg0KICA8c2FtbDpBc3NlcnRpb24geG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiBJRD0iX2Q3MWEzYThlOWZjYzQ1YzllOWQyNDhlZjcwNDkzOTNmYzhmMDRlNWY3NSIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPg0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCI+X2NlM2QyOTQ4YjRjZjIwMTQ2ZGVlMGEwYjNkZDZmNjliNmNmODZmNjJkNzwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiIFJlY2lwaWVudD0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL2luZGV4LnBocD9hY3MiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDctMTdUMDE6MDE6MThaIiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3RAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcnM8L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmV4YW1wbGVyb2xlMTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+'; + + describe('that accepts meta argument', function() { + function CustomStore() {} + + CustomStore.prototype.store = function(req, meta, cb) { + if (req.url === '/error') { return cb(new Error('something went wrong storing state')); } + if (req.url === '/exception') { throw new Error('something went horribly wrong storing state'); } + + if (req.url !== '/me') { return cb(new Error('incorrect req argument')); } + if (meta.identityProviderUrl !== 'http://www.example.com/samlp') { return cb(new Error('incorrect meta.identityProviderUrl argument')); } + + req.customStoreStoreCalled = req.customStoreStoreCalled ? req.customStoreStoreCalled++ : 1; + return cb(null, 'foos7473'); + }; + + CustomStore.prototype.verify = function(req, state, meta, cb) { + if (req.url === '/error') { return cb(new Error('something went wrong verifying state')); } + if (req.url === '/exception') { throw new Error('something went horribly wrong verifying state'); } + + if (state !== 'foos7473') { return cb(new Error('incorrect state argument')); } + if (meta.identityProviderUrl !== 'http://www.example.com/samlp') { return cb(new Error('incorrect meta.identityProviderUrl argument')); } + + req.customStoreVerifyCalled = req.customStoreVerifyCalled ? req.customStoreVerifyCalled++ : 1; + return cb(null, true); + }; + + describe('issuing authorization request', function() { + var strategy = new Strategy({ + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function() {}); + + describe('that redirects to service provider', function() { + var request, url; + + before(function (done) { + chai.passport.use(strategy) + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.url = '/me'; + }) + .authenticate({}); + }); + + it('should be redirected', function() { + expect(url).to.have.string('http://www.example.com/samlp?SAMLRequest='); + expect(url).to.have.string('&RelayState=foos7473'); + }); + + it('should serialize state using custom store', function() { + expect(request.customStoreStoreCalled).to.equal(1); + }); + }); + + describe('that errors due to custom store supplying error', function() { + var request, err; + + before(function (done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + req.url = '/error'; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('something went wrong storing state'); + }); + }); + + describe('that errors due to custom store throwing error', function() { + var request, err; + + before(function (done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + req.url = '/exception'; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('something went horribly wrong storing state'); + }); + }); + }); + + describe('processing response to authorization request', function() { + var strategy = new Strategy({ + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); + + strategy._samlp.validateSamlResponse = function(token, done) { + expect(token).to.be.an('object'); + done(null, { id: '1234' }); + }; + + describe('that was approved', function() { + var request, user, info; + + before(function (done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Hello'); + }); + + it('should verify state using custom store', function() { + expect(request.customStoreVerifyCalled).to.equal(1); + }); + }); + + describe('that errors due to custom store supplying error', function() { + var request, err; + + before(function (done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/error'; + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'foos7473'; + req.method = 'POST'; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('something went wrong verifying state'); + }); + }); + + describe('that errors due to custom store throwing error', function() { + var request, err; + + before(function (done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/exception'; + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'foos7473'; + req.method = 'POST'; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('something went horribly wrong verifying state'); + }); + }); + }); + }); + + describe('that accepts meta argument and supplies state', function() { + function CustomStore() {} + + CustomStore.prototype.verify = function(req, state, meta, cb) { + req.customStoreVerifyCalled = req.customStoreVerifyCalled ? req.customStoreVerifyCalled++ : 1; + return cb(null, true, { returnTo: 'http://www.example.com/' }); + }; + + describe('processing response to authorization request', function() { + + describe('that was approved without info', function() { + var strategy = new Strategy({ + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile); + }); + + strategy._samlp.validateSamlResponse = function(token, done) { + expect(token).to.be.an('object'); + done(null, { id: '1234' }); + }; + + var request, user, info; + + before(function (done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info with state', function() { + expect(info).to.be.an('object'); + expect(Object.keys(info)).to.have.length(1); + expect(info.state).to.be.an('object'); + expect(info.state.returnTo).to.equal('http://www.example.com/'); + }); + + it('should verify state using custom store', function() { + expect(request.customStoreVerifyCalled).to.equal(1); + }); + }); + + describe('that was approved with info', function() { + var strategy = new Strategy({ + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); + + strategy._samlp.validateSamlResponse = function(token, done) { + expect(token).to.be.an('object'); + done(null, { id: '1234' }); + }; + + var request, user, info; + + before(function (done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info with state', function() { + expect(info).to.be.an('object'); + expect(Object.keys(info)).to.have.length(2); + expect(info.message).to.equal('Hello'); + expect(info.state).to.be.an('object'); + expect(info.state.returnTo).to.equal('http://www.example.com/'); + }); + + it('should verify state using custom store', function() { + expect(request.customStoreVerifyCalled).to.equal(1); + }); + }); + }); + }); +}); diff --git a/test/state/samlp.state.session.tests.js b/test/state/samlp.state.session.tests.js new file mode 100644 index 0000000..d998131 --- /dev/null +++ b/test/state/samlp.state.session.tests.js @@ -0,0 +1,441 @@ +var chai = require('chai'); +var uri = require('url'); +var expect = require('chai').expect; +var passport = require('chai-passport-strategy'); +var Strategy = require('../../lib/passport-wsfed-saml2').Strategy; + +chai.use(passport); + +describe('samlp - using default session state store', function() { + + var SAMLResponse = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfOGU4ZGM1ZjY5YTk4Y2M0YzFmZjM0MjdlNWNlMzQ2MDZmZDY3MmY5MWU2IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1Ij4NCiAgPHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj4NCiAgPHNhbWxwOlN0YXR1cz4NCiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+DQogIDwvc2FtbHA6U3RhdHVzPg0KICA8c2FtbDpBc3NlcnRpb24geG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiBJRD0iX2Q3MWEzYThlOWZjYzQ1YzllOWQyNDhlZjcwNDkzOTNmYzhmMDRlNWY3NSIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPg0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCI+X2NlM2QyOTQ4YjRjZjIwMTQ2ZGVlMGEwYjNkZDZmNjliNmNmODZmNjJkNzwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiIFJlY2lwaWVudD0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL2luZGV4LnBocD9hY3MiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDctMTdUMDE6MDE6MThaIiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3RAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcnM8L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmV4YW1wbGVyb2xlMTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+'; + + describe('without session key option', function() { + + describe('issuing authorization request', function() { + var strategy = new Strategy({ + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + state: true + }, function () {}); + + describe('that redirects to service provider', function() { + var request, url; + + before(function(done) { + chai.passport.use(strategy) + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + }) + .authenticate({}); + }); + + it('should be redirected', function() { + var u = uri.parse(url, true); + expect(u.query.RelayState).to.have.length(24); + }); + + it('should save state in session', function() { + var u = uri.parse(url, true); + expect(request.session['samlp:www.example.com'].state).to.have.length(24); + expect(request.session['samlp:www.example.com'].state).to.equal(u.query.RelayState); + }); + }); + + describe('that redirects to service provider with other data in session', function() { + var request, url; + + before(function(done) { + chai.passport.use(strategy) + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + req.session['samlp:www.example.com'] = {}; + req.session['samlp:www.example.com'].foo = 'bar'; + }) + .authenticate({}); + }); + + it('should be redirected', function() { + var u = uri.parse(url, true); + expect(u.query.RelayState).to.have.length(24); + }); + + it('should save state in session', function() { + var u = uri.parse(url, true); + + expect(request.session['samlp:www.example.com'].state).to.have.length(24); + expect(request.session['samlp:www.example.com'].state).to.equal(u.query.RelayState); + }); + + it('should preserve other data in session', function() { + expect(request.session['samlp:www.example.com'].foo).to.equal('bar'); + }); + }); + + describe('that errors due to lack of session support in app', function() { + var request, err; + + before(function(done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('Authentication requires session support when using state. Did you forget to use express-session middleware?'); + }); + }); + }); + + describe('processing response to authorization request', function() { + var strategy = new Strategy({ + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + state: true + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); + + strategy._samlp.validateSamlResponse = function(token, done) { + expect(token).to.be.an('object'); + done(null, { id: '1234' }); + }; + + describe('that was approved', function() { + var request, user, info; + + before(function (done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['samlp:www.example.com'] = {}; + req.session['samlp:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Hello'); + }); + + it('should remove state from session', function() { + expect(request.session['samlp:www.example.com']).to.be.undefined; + }); + }); + + describe('that was approved with other data in the session', function() { + var request, user, info; + + before(function(done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['samlp:www.example.com'] = {}; + req.session['samlp:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.session['samlp:www.example.com'].foo = 'bar'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Hello'); + }); + + it('should preserve other data from session', function() { + expect(request.session['samlp:www.example.com'].state).to.be.undefined; + expect(request.session['samlp:www.example.com'].foo).to.equal('bar'); + }); + }); + + describe('that fails due to state being invalid', function() { + var request, info, status; + + before(function (done) { + chai.passport.use(strategy) + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; + req.method = 'POST'; + req.session = {}; + req.session['samlp:www.example.com'] = {}; + req.session['samlp:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + }) + .authenticate({}); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Invalid authorization request state.'); + }); + + it('should supply status', function() { + expect(status).to.equal(403); + }); + + it('should remove state from session', function() { + expect(request.session['samlp:www.example.com']).to.be.undefined; + }); + }); + + describe('that fails due to provider-specific state not found in session', function() { + var request, info, status; + + before(function(done) { + chai.passport.use(strategy) + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + }) + .authenticate({}); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Unable to verify authorization request state.'); + }); + + it('should supply status', function() { + expect(status).to.equal(403); + }); + }); + + describe('that fails due to provider-specific state lacking state value', function() { + var request, info, status; + + before(function(done) { + chai.passport.use(strategy) + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['samlp:www.example.com'] = {}; + }) + .authenticate({}); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Unable to verify authorization request state.'); + }); + + it('should supply status', function() { + expect(status).to.equal(403); + }); + }); + + describe('that errors due to lack of session support in app', function() { + var request, err; + + before(function (done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('Authentication requires session support when using state. Did you forget to use express-session middleware?'); + }); + }); + }); + }); + + describe('with session key option', function() { + var strategy = new Strategy({ + protocol: 'samlp', + path: '/callback', + realm: 'https://auth0-dev-ed.my.salesforce.com', + identityProviderUrl: 'http://www.example.com/samlp', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + state: true, + sessionKey: 'samlp:example' + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); + + strategy._samlp.validateSamlResponse = function(token, done) { + expect(token).to.be.an('object'); + done(null, { id: '1234' }); + }; + + describe('issuing authorization request', function() { + + describe('that redirects to service provider', function() { + var request, url; + + before(function (done) { + chai.passport.use(strategy) + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + }) + .authenticate({}); + }); + + it('should be redirected', function() { + var u = uri.parse(url, true); + expect(u.query.RelayState).to.have.length(24); + }); + + it('should save state in session', function() { + var u = uri.parse(url, true); + + expect(request.session['samlp:example'].state).to.have.length(24); + expect(request.session['samlp:example'].state).to.equal(u.query.RelayState); + }); + }); + }); + + describe('processing response to authorization request', function() { + + describe('that was approved', function() { + var request, user, info; + + before(function (done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.SAMLResponse = SAMLResponse; + req.body.RelayState = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['samlp:example'] = {}; + req.session['samlp:example']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Hello'); + }); + + it('should remove state from session', function() { + expect(request.session['samlp:example']).to.be.undefined; + }); + }); + }); + }); +}); diff --git a/test/state/wsfed.state.custom.tests.js b/test/state/wsfed.state.custom.tests.js new file mode 100644 index 0000000..aeec369 --- /dev/null +++ b/test/state/wsfed.state.custom.tests.js @@ -0,0 +1,371 @@ +var chai = require('chai'); +var expect = require('chai').expect; +var passport = require('chai-passport-strategy'); +var Strategy = require('../../lib/passport-wsfed-saml2').Strategy; + +chai.use(passport); + +describe('wsfed - using custom session state store', function() { + + describe('that accepts meta argument', function() { + function CustomStore() {} + + CustomStore.prototype.store = function(req, meta, cb) { + if (req.url === '/error') { return cb(new Error('something went wrong storing state')); } + if (req.url === '/exception') { throw new Error('something went horribly wrong storing state'); } + + if (req.url !== '/me') { return cb(new Error('incorrect req argument')); } + if (meta.identityProviderUrl !== 'http://www.example.com/login') { return cb(new Error('incorrect meta.identityProviderUrl argument')); } + + req.customStoreStoreCalled = req.customStoreStoreCalled ? req.customStoreStoreCalled++ : 1; + return cb(null, 'foos7473'); + }; + + CustomStore.prototype.verify = function(req, state, meta, cb) { + if (req.url === '/error') { return cb(new Error('something went wrong verifying state')); } + if (req.url === '/exception') { throw new Error('something went horribly wrong verifying state'); } + + if (state !== 'foos7473') { return cb(new Error('incorrect state argument')); } + if (meta.identityProviderUrl !== 'http://www.example.com/login') { return cb(new Error('incorrect meta.identityProviderUrl argument')); } + + req.customStoreVerifyCalled = req.customStoreVerifyCalled ? req.customStoreVerifyCalled++ : 1; + return cb(null, true); + }; + + describe('issuing authorization request', function() { + var strategy = new Strategy({ + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function() {}); + + describe('that redirects to service provider', function() { + var request, url; + + before(function (done) { + chai.passport.use(strategy) + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.url = '/me'; + }) + .authenticate({}); + }); + + it('should be redirected', function() { + expect(url).to.equal('http://www.example.com/login?wctx=foos7473&wtrealm=urn%3Afixture-test&wa=wsignin1.0&whr='); + }); + + it('should serialize state using custom store', function() { + expect(request.customStoreStoreCalled).to.equal(1); + }); + }); + + describe('that errors due to custom store supplying error', function() { + var request, err; + + before(function (done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + req.url = '/error'; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('something went wrong storing state'); + }); + }); + + describe('that errors due to custom store throwing error', function() { + var request, err; + + before(function (done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + req.url = '/exception'; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('something went horribly wrong storing state'); + }); + }); + }); + + describe('processing response to authorization request', function() { + var strategy = new Strategy({ + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); + + strategy._wsfed.extractToken = function(req) { + expect(req).to.be.an('object'); + return '...'; + }; + + strategy._saml.validateSamlAssertion = function(token, done) { + expect(token).to.equal('...'); + done(null, { id: '1234' }); + }; + + describe('that was approved', function() { + var request, user, info; + + before(function (done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Hello'); + }); + + it('should verify state using custom store', function() { + expect(request.customStoreVerifyCalled).to.equal(1); + }); + }); + + describe('that errors due to custom store supplying error', function() { + var request, err; + + before(function (done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/error'; + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'foos7473'; + req.method = 'POST'; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('something went wrong verifying state'); + }); + }); + + describe('that errors due to custom store throwing error', function() { + var request, err; + + before(function (done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/exception'; + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'foos7473'; + req.method = 'POST'; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('something went horribly wrong verifying state'); + }); + }); + }); + }); + + describe('that accepts meta argument and supplies state', function() { + function CustomStore() {} + + CustomStore.prototype.verify = function(req, state, meta, cb) { + req.customStoreVerifyCalled = req.customStoreVerifyCalled ? req.customStoreVerifyCalled++ : 1; + return cb(null, true, { returnTo: 'http://www.example.com/' }); + }; + + describe('processing response to authorization request', function() { + + describe('that was approved without info', function() { + var strategy = new Strategy({ + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile); + }); + + strategy._wsfed.extractToken = function(req) { + expect(req).to.be.an('object'); + return '...'; + }; + + strategy._saml.validateSamlAssertion = function(token, done) { + expect(token).to.equal('...'); + done(null, { id: '1234' }); + }; + + var request, user, info; + + before(function (done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info with state', function() { + expect(info).to.be.an('object'); + expect(Object.keys(info)).to.have.length(1); + expect(info.state).to.be.an('object'); + expect(info.state.returnTo).to.equal('http://www.example.com/'); + }); + + it('should verify state using custom store', function() { + expect(request.customStoreVerifyCalled).to.equal(1); + }); + }); + + describe('that was approved with info', function() { + var strategy = new Strategy({ + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + store: new CustomStore() + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); + + strategy._wsfed.extractToken = function(req) { + expect(req).to.be.an('object'); + return '...'; + }; + + strategy._saml.validateSamlAssertion = function(token, done) { + expect(token).to.equal('...'); + done(null, { id: '1234' }); + }; + + var request, user, info; + + before(function (done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.url = '/login'; + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'foos7473'; + req.method = 'POST'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info with state', function() { + expect(info).to.be.an('object'); + expect(Object.keys(info)).to.have.length(2); + expect(info.message).to.equal('Hello'); + expect(info.state).to.be.an('object'); + expect(info.state.returnTo).to.equal('http://www.example.com/'); + }); + + it('should verify state using custom store', function() { + expect(request.customStoreVerifyCalled).to.equal(1); + }); + }); + }); + }); +}); diff --git a/test/state/wsfed.state.session.tests.js b/test/state/wsfed.state.session.tests.js new file mode 100644 index 0000000..943c301 --- /dev/null +++ b/test/state/wsfed.state.session.tests.js @@ -0,0 +1,446 @@ +var chai = require('chai'); +var uri = require('url'); +var expect = require('chai').expect; +var passport = require('chai-passport-strategy'); +var Strategy = require('../../lib/passport-wsfed-saml2').Strategy; + +chai.use(passport); + +describe('wsfed - using default session state store', function() { + + describe('without session key option', function() { + + describe('issuing authorization request', function() { + var strategy = new Strategy({ + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + state: true + }, function () {}); + + describe('that redirects to service provider', function() { + var request, url; + + before(function(done) { + chai.passport.use(strategy) + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + }) + .authenticate({}); + }); + + it('should be redirected', function() { + var u = uri.parse(url, true); + expect(u.query.wctx).to.have.length(24); + }); + + it('should save state in session', function() { + var u = uri.parse(url, true); + expect(request.session['wsfed:www.example.com'].state).to.have.length(24); + expect(request.session['wsfed:www.example.com'].state).to.equal(u.query.wctx); + }); + }); + + describe('that redirects to service provider with other data in session', function() { + var request, url; + + before(function(done) { + chai.passport.use(strategy) + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + req.session['wsfed:www.example.com'] = {}; + req.session['wsfed:www.example.com'].foo = 'bar'; + }) + .authenticate({}); + }); + + it('should be redirected', function() { + var u = uri.parse(url, true); + expect(u.query.wctx).to.have.length(24); + }); + + it('should save state in session', function() { + var u = uri.parse(url, true); + + expect(request.session['wsfed:www.example.com'].state).to.have.length(24); + expect(request.session['wsfed:www.example.com'].state).to.equal(u.query.wctx); + }); + + it('should preserve other data in session', function() { + expect(request.session['wsfed:www.example.com'].foo).to.equal('bar'); + }); + }); + + describe('that errors due to lack of session support in app', function() { + var request, err; + + before(function(done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('Authentication requires session support when using state. Did you forget to use express-session middleware?'); + }); + }); + }); + + describe('processing response to authorization request', function() { + var strategy = new Strategy({ + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + state: true + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); + + strategy._wsfed.extractToken = function(req) { + expect(req).to.be.an('object'); + return '...'; + }; + + strategy._saml.validateSamlAssertion = function(token, done) { + expect(token).to.equal('...'); + done(null, { id: '1234' }); + }; + + describe('that was approved', function() { + var request, user, info; + + before(function (done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['wsfed:www.example.com'] = {}; + req.session['wsfed:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Hello'); + }); + + it('should remove state from session', function() { + expect(request.session['wsfed:www.example.com']).to.be.undefined; + }); + }); + + describe('that was approved with other data in the session', function() { + var request, user, info; + + before(function(done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['wsfed:www.example.com'] = {}; + req.session['wsfed:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.session['wsfed:www.example.com'].foo = 'bar'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Hello'); + }); + + it('should preserve other data from session', function() { + expect(request.session['wsfed:www.example.com'].state).to.be.undefined; + expect(request.session['wsfed:www.example.com'].foo).to.equal('bar'); + }); + }); + + describe('that fails due to state being invalid', function() { + var request, info, status; + + before(function (done) { + chai.passport.use(strategy) + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; + req.method = 'POST'; + req.session = {}; + req.session['wsfed:www.example.com'] = {}; + req.session['wsfed:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + }) + .authenticate({}); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Invalid authorization request state.'); + }); + + it('should supply status', function() { + expect(status).to.equal(403); + }); + + it('should remove state from session', function() { + expect(request.session['wsfed:www.example.com']).to.be.undefined; + }); + }); + + describe('that fails due to provider-specific state not found in session', function() { + var request, info, status; + + before(function(done) { + chai.passport.use(strategy) + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; + req.method = 'POST'; + req.session = {}; + }) + .authenticate({}); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Unable to verify authorization request state.'); + }); + + it('should supply status', function() { + expect(status).to.equal(403); + }); + }); + + describe('that fails due to provider-specific state lacking state value', function() { + var request, info, status; + + before(function(done) { + chai.passport.use(strategy) + .fail(function(i, s) { + info = i; + status = s; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; + req.method = 'POST'; + req.session = {}; + req.session['wsfed:www.example.com'] = {}; + }) + .authenticate({}); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Unable to verify authorization request state.'); + }); + + it('should supply status', function() { + expect(status).to.equal(403); + }); + }); + + describe('that errors due to lack of session support in app', function() { + var request, err; + + before(function (done) { + chai.passport.use(strategy) + .error(function(e) { + err = e; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG'; + req.method = 'POST'; + }) + .authenticate({}); + }); + + it('should error', function() { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('Authentication requires session support when using state. Did you forget to use express-session middleware?'); + }); + }); + }); + }); + + describe('with session key option', function() { + var strategy = new Strategy({ + path: '/callback', + realm: 'urn:fixture-test', + identityProviderUrl: 'http://www.example.com/login', + thumbprints: ['5ca6e1202eafc0a63a5b93a43572eb2376fed309'], + state: true, + sessionKey: 'wsfed:example' + }, + function (profile, done) { + return done(null, profile, { message: 'Hello' }); + }); + + strategy._wsfed.extractToken = function(req) { + expect(req).to.be.an('object'); + return '...'; + }; + + strategy._saml.validateSamlAssertion = function(token, done) { + expect(token).to.equal('...'); + done(null, { id: '1234' }); + }; + + describe('issuing authorization request', function() { + + describe('that redirects to service provider', function() { + var request, url; + + before(function (done) { + chai.passport.use(strategy) + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + request = req; + req.session = {}; + }) + .authenticate({}); + }); + + it('should be redirected', function() { + var u = uri.parse(url, true); + expect(u.query.wctx).to.have.length(24); + }); + + it('should save state in session', function() { + var u = uri.parse(url, true); + + expect(request.session['wsfed:example'].state).to.have.length(24); + expect(request.session['wsfed:example'].state).to.equal(u.query.wctx); + }); + }); + }); + + describe('processing response to authorization request', function() { + + describe('that was approved', function() { + var request, user, info; + + before(function (done) { + chai.passport.use(strategy) + .success(function(u, i) { + user = u; + info = i; + done(); + }) + .req(function(req) { + request = req; + + req.body = {}; + req.body.wresult = '...'; + req.body.wctx = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.method = 'POST'; + req.session = {}; + req.session['wsfed:example'] = {}; + req.session['wsfed:example']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK'; + req.get = function(){ + return ''; + }; + }) + .authenticate({}); + }); + + it('should supply user', function() { + expect(user).to.be.an('object'); + expect(user.id).to.equal('1234'); + }); + + it('should supply info', function() { + expect(info).to.be.an('object'); + expect(info.message).to.equal('Hello'); + }); + + it('should remove state from session', function() { + expect(request.session['wsfed:example']).to.be.undefined; + }); + }); + }); + }); +}); diff --git a/test/test-auth0-2.cer b/test/test-auth0-2.cer new file mode 100644 index 0000000..68e12e1 --- /dev/null +++ b/test/test-auth0-2.cer @@ -0,0 +1 @@ +MIIC1jCCAb6gAwIBAgIQGV2w2uPcfZBGYrFfGhwbSDANBgkqhkiG9w0BAQsFADAnMSUwIwYDVQQDExxBREZTIFNpZ25pbmcgLSBhZGZzLm15LmxvY2FsMB4XDTE3MDYxNTE5NDQyNVoXDTE4MDYxNTE5NDQyNVowJzElMCMGA1UEAxMcQURGUyBTaWduaW5nIC0gYWRmcy5teS5sb2NhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN6JrhRIYQeBrFM8bRnzEAFidm+RnFnhAhRv5GBUy4Fjd+fQ0ro4zVMZoyw+hhs/iHlSt3UNzuQZaXU+o0LpEO+LUDw2WiaUOVD8H6PO17Fj9eT85+rGzXNKtZulRjWPxNM8bQfcwhcEt4jcYoK46YirCLpu7jMd1bWY6C/E0Ys8SHfLTb7AZYxHo8Elscof3nu65zHrYIErguD/kB9Fk1tKmfCwGO0Bhi9f8B8u+TuQ6GA4UW1N+FcBxfcj8siJ84BRQZu9xFDMHfICIEfBbUg+Y8PtagCUEqa5zoT9XIXeT+mhSrL+HfeH1kKjxW436HjnDMCGLtwmq7E7WXr1d60CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAS7tJReVEPig+LLxNfvMK3xGSvQnKVUI8GpzWRmHDbZVWeLlwGa0xl8GJf80022CeAVFdVw2kGbhD7UeaPsuAGT6wEkvcjOQ7BcHY+jSMOiiG0JvFBl5Do0XHnplX4tRxjuTPXn5bpuCptScXAWbOJmeuenDx0RW4cdLLWzRtuKsrm8IzKJjTET3FoE0ufhma7gNpVXzySTN14EwBFmkEmvb5oHrShTAjOxuR1Ucd1rqiy65tiw2OWnSoQeu6wCAL2eaWKBg4O1SlrRwvpj2lcuRhcJuJTY0pRoZxRzPIDSeX3DkyF+6QkK+OetzLleOm+0dLIxUeG5dVO8qrjZCNvA== \ No newline at end of file diff --git a/test/test-auth0.cer b/test/test-auth0.cer new file mode 100644 index 0000000..a5ba737 --- /dev/null +++ b/test/test-auth0.cer @@ -0,0 +1 @@ +MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo= \ No newline at end of file diff --git a/test/test-auth0.key b/test/test-auth0.key new file mode 100644 index 0000000..663a737 --- /dev/null +++ b/test/test-auth0.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxmJWY0eJcuV2uBtLnQ4004fuknbODo5xIyRhkYNkls5n9OrB +q4Lok6cjv7G2Q8mxAdlIUmzhTSyuNkrMMKZrPaMsAkNKE/aNpeWuSLXqcMs8T/8g +YCDcEmC5KYEJakNtKb3ZX2FKwT4yHHpsNomLDzJD5DyJKbRpNBm2no7ggIy7TQRJ +2H00mogQIQu8/fUANXVeGPshvLJU8MXEy/eiXkHJIT3DDA4VSr/C/tfP0tGJSNTM +874urc4zej+4INuTuMPtesZS47J0AsPxQuxengS4M76cVt5cH+Iqd1nKe5UqiSKv +LCXacPYg/T/Kdx0tBnwHIjKo/cbzZ+r+XynsCwIDAQABAoIBAFPWWwu5v6x+rJ1B +a8MDre93Eqty6cHdEJL5XQJRtMDGmcg3LYF94SwFBmaMg6pCIjvVx2qN+OjUaQso +sQIeUlPKEV8jcLrfBx2E4xJ3Tow8V1C3UMdPG7Hojler4H633/oz8RkN1Lm1vxep +5PFnTw0tAOQDcTPeulb6RuLbHqU0FEnf/jVOMhtPLcMAwJ3fkAJQ+ljFW2VKCQ83 +d+ci1p+NHY/dbGLSR4lK58mVghcRMO3zhe5scrbECHJMfT6fCb2TXdjaueFUGC6+ +fqUXvDj8HRfUilzTegNq8ZhwgMSw1HeX/PuiczSKc3aHYSsohMBugTErnkW+qF4Z +kE+kxgECgYEA/sm7umcyFuZME+RWYL8Gsp8agH1OGEgsmIiMi1z6RTlTmdR8fN18 +ItzXyW+363VZln/1b5wCaPdLIxgASxybLAaxnKAXfmL7QvyVAaMwxj7N0ogvMQoN +x2VuSGZSam2+LFVIMWHq1C+3fvVnCDLm6oHvIMK/zvEsPBBtz+L6rlECgYEAx1Pr +KogaGHCi1XgsrNv9aFaayRvmhzZbmiigF0iWKAd3KKww94BdyyGSVfMfyL23LAbM +QDCrDNGpYAnpNZo/cL+OcGPYzlPsWDBrJub1HOA/H3WQlP4oEcfdbmJZhIkEwTGF +HaCHynEu4ekiCrWz9+XVNCquTyqnmaVDEzAfEZsCgYA8jQbfUt0Vkh+sboyUq3FV +C/jJZn4jyStICNOV3z/fKbOTkGsRZbW1t1RVHAbSn23uFXTn1GTCO1sQ+QhA0YiT +Gvgk5+sNb0qVbd+fpv/VbWGO0iyc8+24YIOoEyEtB+21LYNdsQ6U5M4wDvQwf6Bf +RQfmekIJVUmU8LaYPDIlMQKBgDSRiT/aTSeM7STnYMDl89sEnCXV2eJnD5mEhVQe +rJs5/M8ZOoDLtfDQlctdJ1DF1/0gfdWgADyNPuI5OuwMFhciLequKoufzoEjo97K +onJPIdamJs9kiCTIVTm7bmhpyns5GCZMJAPb/cVOus+gRCpozuXHK9ltIm5/C0WQ +N2FpAoGBAOss6RN2krieqbn1mG8e2v5mMUd0CJkiJu2y5MnF3dYHXSQ3/ePAh/Yg +JOthpgYgBh+mV0DLqJhx/1DLS/xiqcoHDlndQDmYbtvvY7RlMo00+nGzkRVOfrqy +hC+1KsYHGPbSQixNQXtvFbAAVMSo+RRBkVGINYGDFnlQUpkppYRk +-----END RSA PRIVATE KEY----- diff --git a/test/test-auth0.pem b/test/test-auth0.pem new file mode 100644 index 0000000..47e8e14 --- /dev/null +++ b/test/test-auth0.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNV +BAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQG +EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0x +MjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1 +dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UE +CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbO +Z/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDL +PE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICM +u00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LR +iUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuV +Kokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNV +HQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeB +jTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTES +MBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu +Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDb +cRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6Fsxo +eZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ53 +2ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6Tf +KgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3Wk +mcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo= +-----END CERTIFICATE----- diff --git a/test/test-decryption.key b/test/test-decryption.key new file mode 100644 index 0000000..349d6aa --- /dev/null +++ b/test/test-decryption.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEApv0aJ9/4q9A/Cj16g5tJbs01hkWtSjFGfvioxx19nCS7Abc7 +wQaLxf5Sw202DBCUxV9i32UoHvWIlKUo4GIE9IU/vM/2MW1Od8lxpG929g+ggLhj +zW27ouQ03d7X/fU2n5Hql3sXot8daq5/4H6HIckJdqx13omUah5v3j56amP2WbzA +c1veyv/zzf3goncVQF7E/NDmukot7t3l+bgQo7+LISlYdSV/t1/3IYPqbvU/Gj7y +vwOqrsDkSEZf/OTpK7BniP+zOLPKf9xe6WUaDc8Cxz1dctPjnkDUoQHHl5Masxzs +lDe4NSqp8N/i+PCU0FwiPGDZfd0zSwlWT/mvTQIDAQABAoIBAFd9qtOTjHVwsWbL +GUKU/N950v5byVRjBt2FqOBSOjqyH+zhNQSISnldK73KnZOqqFfVBzemh2PZH0tK +UWVm1dPLcL6bcxEohQFL4SfXYsu/Gkf21AuQ38uTp4NLu30Kmij2yaO+UX77SS1Y +6hUUFfcEntm3uDX1fXYwJkqaD4dnqX+qSUSUi79j37oyGrjQ43B7/Ov8RX+tpl6O +pHbcnQEfy/qNz4RvQxGiAKiOkR69dSASh4aKhtj/hCSYEZcCKd0HqkSTiGWS3I3S +IEIkgCzfuMZZX0GwY4XO5IvoOe4R4X8UhLg/N1nYjlFb+pr7IreXikOXfqGSU64P +IIBLyCECgYEA2WUfCRaaNmfX9BKKyKxdIVCJcBSi7qCEFYu4+GoX9+zSEpL/q34S +m7avdsr6lvhtPjnmN/EuuuenrPvQTksiW+s0+3h82CsFjnA9srG1/uWtzeLOMInr +l4KU0A4zFjRV31Rr7Hw8WiY/SUt1OGxDr/zJlXsZdGMy0WHQRuHPm0kCgYEAxKR8 +XkUZ/P40NdBiy+SwLfmp+Y4C2azTRNAqiMvGoKzfIhk7aWqVbtUmEpa0USJ1ZT0Q +4kfL0M25RwOatit+1ZOOEtextNId17wJ1nN05+1X//M/TndzuKbGGiD8WglhGJ66 +CbaJ70x3JRPFXslwktNFtuCu7ZHEiNdBrLdWj+UCgYAh2D9jr9QkYjhZtEVNBqCd +Hie3fk77bZwCqrUKX6IJpCH0aFRLg54sBd39VY570INZa7Quw2quCICvSqjcd5AK +1WxzNgfhs5jy2wCQAGDAJUvQwN2u6tn1xYubdIp5i35O/Zqrv4+5zTiPFaNTPG+x +R16u2fJVj1gLBvpg/qjOmQKBgQCmAfLYpt+kvHoZQD5XwU2W0qHTgzcWyxdkjZSK +/fVGqgqJv+FvQkK7WYiVwtKheETBXgdF8LS5JyQWNo3C4v9lkPctIxjr/UKiYSWw +/LGiJrXW52T3elKgfXLaliRUYnytCzslfgv+kIA5NfK27bpyyZeeJxqmhxvzJj5U +cEbMnQKBgQCg1APAfp3iJB8dVdLtASFuR90/FvutPx9PX+8BpbKzIi760C5um9O3 +sb/kyiulMJtRK7FNZt34DGUqCmsascB5ia37uDq3b2BowELrDuD4uP+nIRojPSgb +ZqumSC1ns30M4yx05umvS7EZQf17xM0f5FCofvHAhnSVQwpBIAK8Ew== +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000..c3588ad --- /dev/null +++ b/test/utils.js @@ -0,0 +1,84 @@ +var expect = require('chai').expect; + +var lib = require('../lib/passport-wsfed-saml2'); +var utils = require('../lib/passport-wsfed-saml2/utils'); + +describe('utils', function () { + describe('parseSamlAssertion', function () { + it('should work', function (done) { + var assertion = utils.parseSamlAssertion(''); + expect(assertion.childNodes.length) + .to.equal(1); + done(); + }); + + it('should throw an error with more details', function (done) { + function parse() { + try { + utils.parseSamlAssertion('
'); + } catch (e) { + return e; + } + } + + var err = parse(); + expect(err.name) + .to.equal('SamlAssertionParserError'); + expect(err.detail) + .to.equal('end tag name: div is not match the current start tagName:t:RequestSecurityTokenResponse'); + done(); + }); + }); + + describe('parseSamlResponse', function () { + it('should work', function (done) { + var response = utils.parseSamlResponse(''); + expect(response.childNodes.length) + .to.equal(1); + done(); + }); + + it('should throw an error with more details', function (done) { + function parse() { + try { + utils.parseSamlResponse('
'); + } catch (e) { + return e; + } + } + + var err = parse(); + expect(err.name) + .to.equal('SamlResponseParserError'); + expect(err.detail) + .to.equal('end tag name: div is not match the current start tagName:t:RequestSecurityTokenResponse'); + done(); + }); + }); + + describe('parseWsFedResponse', function () { + it('should work', function (done) { + var response = utils.parseWsFedResponse(''); + expect(response.childNodes.length) + .to.equal(1); + done(); + }); + + it('should throw an error with more details', function (done) { + function parse() { + try { + utils.parseWsFedResponse('
'); + } catch (e) { + return e; + } + } + + var err = parse(); + expect(err.name) + .to.equal('WSFederationResultParseError'); + expect(err.detail) + .to.equal('end tag name: div is not match the current start tagName:t:RequestSecurityTokenResponse'); + done(); + }); + }); +}); diff --git a/test/wsfed-result-wstrust13.xml b/test/wsfed-result-wstrust13.xml new file mode 100644 index 0000000..4f383a5 --- /dev/null +++ b/test/wsfed-result-wstrust13.xml @@ -0,0 +1,16 @@ +2015-07-23T15:40:26.113Z2015-07-23T16:40:26.113Z
http://dev.pms.baxon.net/
http://dev.pms.baxon.net/1266urn:oasis:names:tc:SAML:1.0:cm:beareradminfhermida@baxonpe.com6SWgcwiTgl1oclmMGiV0p/QQ2hi9irdIbQuPhsvcsHY=0Wg17usFmMpPNDlPcyXvP9f6i2kQ3RDRvkebBkrEZkYYfmyj8VUhGrkrYRiyGPZNp8jEkbbt/tujc4lOkYB03rpj3FvUx+v8Y/RZbPyfCjXR9FLdWfXwhkz2HW1+n7vqwNxpuLRZDXmOiT1RgSYoLG9A7EgBqMtRZBXS75+rWZfCGqYk9KN+NoSUJnRepdA3BquQXq2zvPO/NwUtPNhfDiE763Wx7AgS1Ni3WO+Yqc0lFA04LJ4uU1KPVKHaY48nSWRLGMJJIF65qEHLdJXl164W72vAXGkZutFh46diNu7g2U+0SHoz04BewTcLR1HfcMo955O4Y1PZz32bw+TTuQ==MIIC8DCCAdigAwIBAgIQUwBSjcy73ZVOX5nV9aGv0zANBgkqhkiG9w0BAQUFADAcMRowGAYDVQQDExFkZXYucG1zLmJheG9uLm5ldDAeFw0xMjA5MTgxODEzMjhaFw0xMzA5MTkwMDEzMjhaMBwxGjAYBgNVBAMTEWRldi5wbXMuYmF4b24ubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ldGxCKIA5sIbwsl9WNpF59jVFG7UZvKjPixxNoxnUHKHll5u14XA1eJvZUgPdOSjRTMTnWN9YpMLumC2PWzNg7PKDNMP6uVnFkmdXI+q31b5WjYkuKOyaGdjFEGRMcr+vqSrcnkUWujXL1t3P+RoabN5svduYaQF20GU5I52EOhzzK+ogE6zHHFaHibjiFQ4ww/Ip9cytCzCsfl258RkauJ3xCVCTVji9MpwmX/1Rj8sma54FN+u2MB9i8GcGrN+ZFxoM98WX6GA1/5B3ZCx1BgHz0D3dXhCi4xU7N7yvkYC4TwHkOjLxmkjz2UP/jm28/R0NNO/HdbFPNFtZ66/wIDAQABoy4wLDALBgNVHQ8EBAMCBPAwHQYDVR0OBBYEFPbqbWG48yDRzE3kutclDzPN3pDnMA0GCSqGSIb3DQEBBQUAA4IBAQB6QIauvKs8bzhI5YZHOdqAhINVbA1m4ulJzlLXbPDbwfD5U4ginePbLxPvvP3jfTujaztD7YK9bxdFTFWdvCSX+RV1zK8wvNZSRV4d3F7ZSXZzmipoInUsTh9U+grqdpdcMddKge/6RDubLD92k6aSKLrOyrFj0oJfkqFgXJnPpVqIlfqjjXRtQ90RUJQJh4qssOHC2a/cwgeBsFj2ei8Rq4tFIo5QO8zDtvKz7E3ZBFiclWjhcZ3t8u2M7JSRk/bk0tTiQyFFzzDx7nZvi8qP00l3ynNOmsz2SeHkSAPrDQ4JRee50ULOuPCC5ySZS326gsq/UXz8zQybp6fFGW1B + + + _b996a6d2-0556-4292-ab63-bcbb183a1eca + + + + + _b996a6d2-0556-4292-ab63-bcbb183a1eca + + +urn:oasis:names:tc:SAML:1.0:assertion +http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue +http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer +
+
diff --git a/test/wsfed.tests.js b/test/wsfed.tests.js new file mode 100644 index 0000000..1950079 --- /dev/null +++ b/test/wsfed.tests.js @@ -0,0 +1,154 @@ +var expect = require('chai').expect; +var server = require('./fixture/wsfed-server'); +var request = require('request'); +var cheerio = require('cheerio'); + +describe('wsfed', function () { + before(function (done) { + server.start(done); + }); + + after(function (done) { + server.close(done); + }); + + describe('normal flow', function () { + var user, r, bod, $; + + before(function (done) { + request.get({ + jar: request.jar(), + uri: 'http://localhost:5050/login?wa=wsignin1.0&wtrealm=urn:fixture-test' + }, function (err, response, b){ + if(err) return done(err); + expect(response.statusCode) + .to.equal(200); + + + $ = cheerio.load(b); + var wresult = $('input[name="wresult"]').attr('value'); + var wa = $('input[name="wa"]').attr('value'); + + request.post({ + jar: request.jar(), + uri: 'http://localhost:5050/callback', + form: { wresult: wresult, wa: wa } + }, function(err, response, body) { + if(err) return done(err); + + r = response; + bod = body; + done(); + }); + }); + }); + + it('should be valid signature', function(){ + expect(r.statusCode) + .to.equal(200); + }); + + it('should return a valid user', function(){ + var user = JSON.parse(bod); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier']) + .to.equal(server.fakeUser.id); + expect(user['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']) + .to.equal(server.fakeUser.emails[0].value); + }); + + }); + + describe('wresult without RequestedSecurityToken', function () { + var user, r, bod, $; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5050/callback', + form: { wresult: '' } + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should return a 400', function(){ + expect(r.statusCode) + .to.equal(400); + }); + }); + + describe('missing wresult in POST', function () { + var user, r, bod, $; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5050/callback' + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should redirect to idp', function(){ + expect(r.statusCode) + .to.equal(302); + }); + }); + + describe('invalid wresult in POST', function () { + var user, r, bod, $; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5050/callback', + form: { wresult: 'foo' } + }, function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should return a 400', function(){ + expect(r.statusCode) + .to.equal(400); + }); + }); + + describe('invalid wresult (xml) in POST', function () { + var user, r, bod, $; + + before(function (done) { + request.post({ + jar: request.jar(), + uri: 'http://localhost:5050/callback/wresult-with-invalid-xml', + form: { wresult: 'urn:fixture-test12345678urn:oasis:names:tc:SAML:1.0:cm:bearer12345678jfoo@gmail.comJohn FooJohnFoo12345678urn:oasis:names:tc:SAML:1.0:cm:bearerfxxeOWyp3M3cVfglUEg0Fc0mVAm7QVCyrSX2Kflq8VE=ETv7SqrEoHYP1FTLcdylyDZotyJ1uuNNCLo6sw4cm4YAnGz/OYUIssUb0s82C3NCfV5ifvryr5khnZCNfRvEWJPsIZAtaSPHeeO+x3ajIDd/qfklNBHpdEYMP2WbcqPA6pYeh+OHgAlG6srsLDO8fMymUa/T8yACIU7cwnouEaYESWRU2fqKOXpeUxB/pENiY+qxPTvxzRYld5OlR+sNAJFPIvl3V5G+vw0mx+7tZteKq7yX0djpwEoFfXAcMzvLoqLqENjxPanmVPv7qvv7dIdI0kPE6jret50sHkHpQ7XZJmGi6cNc+/kvhSHXhD3vJ0u3BP/qCCPYPHz42z+KIw==MIIEDzCCAvegAwIBAgIJALr9HwgrQ7GeMA0GCSqGSIb3DQEBBQUAMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDAeFw0xMjEyMjkxNTMwNDdaFw0xMzAxMjgxNTMwNDdaMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZiVmNHiXLldrgbS50ONNOH7pJ2zg6OcSMkYZGDZJbOZ/TqwauC6JOnI7+xtkPJsQHZSFJs4U0srjZKzDCmaz2jLAJDShP2jaXlrki16nDLPE//IGAg3BJguSmBCWpDbSm92V9hSsE+Mhx6bDaJiw8yQ+Q8iSm0aTQZtp6O4ICMu00ESdh9NJqIECELvP31ADV1Xhj7IbyyVPDFxMv3ol5BySE9wwwOFUq/wv7Xz9LRiUjUzPO+Lq3OM3o/uCDbk7jD7XrGUuOydALD8ULsXp4EuDO+nFbeXB/iKndZynuVKokirywl2nD2IP0/yncdLQZ8ByIyqP3G82fq/l8p7AsCAwEAAaOBxzCBxDAdBgNVHQ4EFgQUHI2rUXeBjTv1zAllaPGrHFcEK0YwgZQGA1UdIwSBjDCBiYAUHI2rUXeBjTv1zAllaPGrHFcEK0ahZqRkMGIxGDAWBgNVBAMTD2F1dGgwLmF1dGgwLmNvbTESMBAGA1UEChMJQXV0aDAgTExDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZIIJALr9HwgrQ7GeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFrXIhCy4T4eGrikb0R2wHv/uS548r3pZyBV0CDbcRwAtbnpJMvkGFqKVp4pmyoIDSVNK/j+sLEshB20XftezHZyRJbCUbtKvXQ6FsxoeZMlN0ITYKTaoBZKhUxxj90otAhNC58qwGUPqt2LewJhHyLucKkGJ1mQ3b5xKZ532ToufouH9VLhig3H1KnxWo/zMD6Ke8cCk6qO9htuhI06s3GQGS1QWQtAmm17C6TfKgDwQFZwhqHUUZnwKRH8gU6OgZsvhgV1B7H5mjZcu57KMiDBekU9MEY0DCVTN3WkmcTII668zLsJrkNX6PEfck1AMBbVE6pEUKcWwq3uaLvlAUo=' } + }, + function(err, response, body) { + if(err) return done(err); + r = response; + bod = body; + done(); + }); + }); + + it('should return a 400', function(){ + expect(r.statusCode) + .to.equal(400); + }); + + it('should be recognized as an invalid xml', function(){ + var err = JSON.parse(bod); + expect(err.message) + .to.equal('wresult should be a valid xml');; + }); + }); +});