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');; + }); + }); +});