diff --git a/lib/passport-wsfed-saml2/strategy.js b/lib/passport-wsfed-saml2/strategy.js
index ffd7972..feb710d 100644
--- a/lib/passport-wsfed-saml2/strategy.js
+++ b/lib/passport-wsfed-saml2/strategy.js
@@ -56,36 +56,32 @@ util.inherits(WsFedSaml2Strategy, Strategy);
WsFedSaml2Strategy.prototype._authenticate_saml = function (req, state) {
var self = this;
- if (req.body.wresult.indexOf('<') === -1) {
- return self.fail('wresult should be a valid xml', 400);
- }
-
- var token = self._wsfed.extractToken(req);
- if (!token) {
- return self.fail('missing RequestedSecurityToken element', 400);
- }
-
- self._saml.validateSamlAssertion(token, function (err, profile) {
- if (err) {
- return self.error(err);
- }
-
- var verified = function (err, user, info) {
+ self._wsfed.retrieveToken(req, function(err, token) {
+ if (err) return self.fail(err, err.status || 400);
+
+ self._saml.validateSamlAssertion(token, function (err, profile) {
if (err) {
return self.error(err);
}
- if (!user) {
- return self.fail(info);
- }
+ var verified = function (err, user, info) {
+ if (err) {
+ return self.error(err);
+ }
- info = info || {};
- if (state) { info.state = state; }
- self.success(user, info);
- };
+ if (!user) {
+ return self.fail(info);
+ }
- self._verify(profile, verified);
+ info = info || {};
+ if (state) { info.state = state; }
+ self.success(user, info);
+ };
+
+ self._verify(profile, verified);
+ });
});
+
};
WsFedSaml2Strategy.prototype._authenticate_jwt = function (req, state) {
diff --git a/lib/passport-wsfed-saml2/wsfederation.js b/lib/passport-wsfed-saml2/wsfederation.js
index 54f28ad..cbe3455 100644
--- a/lib/passport-wsfed-saml2/wsfederation.js
+++ b/lib/passport-wsfed-saml2/wsfederation.js
@@ -1,6 +1,9 @@
var xmldom = require('xmldom');
var xtend = require('xtend');
var qs = require('querystring');
+var xpath = require('xpath');
+
+var AuthenticationFailedError = require('./errors/AuthenticationFailedError');
var WsFederation = module.exports = function WsFederation (realm, homerealm, identityProviderUrl, wreply) {
this.realm = realm;
@@ -39,6 +42,56 @@ WsFederation.prototype = {
}
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 = new xmldom.DOMParser().parseFromString(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;
}
};
diff --git a/test/interop.tests.js b/test/interop.tests.js
index e6a03ff..9ca0c03 100644
--- a/test/interop.tests.js
+++ b/test/interop.tests.js
@@ -345,6 +345,69 @@ describe('interop', function () {
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=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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';
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