Comparing version 0.1.1 to 0.2.0
// Generated by CoffeeScript 1.7.1 | ||
var IdentityProvider, ServiceProvider, async, certificate_to_keyinfo, check_saml_signature, check_status_success, create_authn_request, create_logout_request, create_metadata, crypto, decrypt_assertion, get_name_id, get_session_index, parseString, parse_assertion_attributes, parse_response_header, pretty_assertion_attributes, to_error, url, util, xmlbuilder, xmlcrypto, xmldom, xmlenc, zlib, _, | ||
var IdentityProvider, ServiceProvider, async, certificate_to_keyinfo, check_saml_signature, check_status_success, create_authn_request, create_logout_request, create_metadata, crypto, decrypt_assertion, get_name_id, get_session_index, parseString, parse_assertion_attributes, parse_authn_response, parse_response_header, pretty_assertion_attributes, sign_get_request, to_error, url, util, xmlbuilder, xmlcrypto, xmldom, xmlenc, zlib, _, | ||
__slice = [].slice, | ||
@@ -79,3 +79,3 @@ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; | ||
create_logout_request = function(issuer, name_id, session_index) { | ||
create_logout_request = function(issuer, name_id, session_index, destination) { | ||
return xmlbuilder.create({ | ||
@@ -88,2 +88,3 @@ 'samlp:LogoutRequest': { | ||
'@IssueInstant': (new Date()).toISOString(), | ||
'@Destination': destination, | ||
'saml:Issuer': issuer, | ||
@@ -96,2 +97,14 @@ 'saml:NameID': name_id, | ||
sign_get_request = function(saml_request, private_key) { | ||
var data, sign; | ||
data = "SAMLRequest=" + encodeURIComponent(saml_request) + "&SigAlg=" + encodeURIComponent('http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'); | ||
sign = crypto.createSign('RSA-SHA256'); | ||
sign.update(data); | ||
return { | ||
SAMLRequest: saml_request, | ||
SigAlg: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', | ||
Signature: sign.sign(private_key, 'base64') | ||
}; | ||
}; | ||
certificate_to_keyinfo = function(use, certificate) { | ||
@@ -194,4 +207,11 @@ var cert_data; | ||
parse_response_header = function(dom) { | ||
var attr, response, response_header, _i, _len, _ref; | ||
response = dom.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:protocol', 'Response'); | ||
var attr, response, response_header, response_type, _i, _j, _len, _len1, _ref, _ref1; | ||
_ref = ['Response', 'LogoutResponse']; | ||
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
response_type = _ref[_i]; | ||
response = dom.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:protocol', response_type); | ||
if (response.length > 0) { | ||
break; | ||
} | ||
} | ||
if (response.length !== 1) { | ||
@@ -201,5 +221,5 @@ throw new Error("Expected 1 Response; found " + response.length); | ||
response_header = {}; | ||
_ref = response[0].attributes; | ||
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
attr = _ref[_i]; | ||
_ref1 = response[0].attributes; | ||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { | ||
attr = _ref1[_j]; | ||
switch (attr.name) { | ||
@@ -322,2 +342,32 @@ case "Version": | ||
parse_authn_response = function(saml_response, sp_private_key, idp_certificate, cb) { | ||
var decrypted_assertion, user; | ||
user = {}; | ||
decrypted_assertion = null; | ||
return async.waterfall([ | ||
function(cb_wf) { | ||
return decrypt_assertion(saml_response, sp_private_key, cb_wf); | ||
}, function(result, cb_wf) { | ||
decrypted_assertion = (new xmldom.DOMParser()).parseFromString(result); | ||
return check_saml_signature(result, idp_certificate, cb_wf); | ||
}, function(cb_wf) { | ||
return async.lift(get_name_id)(decrypted_assertion, cb_wf); | ||
}, function(name_id, cb_wf) { | ||
user.name_id = name_id; | ||
return async.lift(get_session_index)(decrypted_assertion, cb_wf); | ||
}, function(session_index, cb_wf) { | ||
user.session_index = session_index; | ||
return async.lift(parse_assertion_attributes)(decrypted_assertion, cb_wf); | ||
}, function(assertion_attributes, cb_wf) { | ||
user = _.extend(user, pretty_assertion_attributes(assertion_attributes)); | ||
user = _.extend(user, { | ||
attributes: assertion_attributes | ||
}); | ||
return cb_wf(null, { | ||
user: user | ||
}); | ||
} | ||
], cb); | ||
}; | ||
module.exports.ServiceProvider = ServiceProvider = (function() { | ||
@@ -329,2 +379,3 @@ function ServiceProvider(issuer, private_key, certificate) { | ||
this.create_metadata = __bind(this.create_metadata, this); | ||
this.create_logout_url = __bind(this.create_logout_url, this); | ||
this.create_login_url = __bind(this.create_login_url, this); | ||
@@ -349,16 +400,26 @@ } | ||
ServiceProvider.prototype.assert = function(identity_provider, request_body, cb) { | ||
var decrypted_assertion, saml_response, user; | ||
ServiceProvider.prototype.assert = function() { | ||
var cb, decrypted_assertion, get_request, identity_provider, request_body, response, saml_response, _i; | ||
identity_provider = arguments[0], request_body = arguments[1], get_request = 4 <= arguments.length ? __slice.call(arguments, 2, _i = arguments.length - 1) : (_i = 2, []), cb = arguments[_i++]; | ||
get_request = get_request[0]; | ||
if ((request_body != null ? request_body.SAMLResponse : void 0) == null) { | ||
return setImmediate(cb, new Error("Request body does not contain SAMLResponse.")); | ||
} | ||
saml_response = (new xmldom.DOMParser()).parseFromString(new Buffer(request_body.SAMLResponse, 'base64').toString()); | ||
saml_response = null; | ||
decrypted_assertion = null; | ||
user = {}; | ||
response = {}; | ||
return async.waterfall([ | ||
function(cb_wf) { | ||
var raw; | ||
raw = new Buffer(request_body.SAMLResponse, 'base64'); | ||
if (get_request) { | ||
return zlib.inflateRaw(raw, cb_wf); | ||
} | ||
return setImmediate(cb_wf, null, raw); | ||
}, function(response_buffer, cb_wf) { | ||
saml_response = (new xmldom.DOMParser()).parseFromString(response_buffer.toString()); | ||
return async.lift(parse_response_header)(saml_response, cb_wf); | ||
}, (function(_this) { | ||
return function(response_header, cb_wf) { | ||
user = { | ||
response = { | ||
response_header: response_header | ||
@@ -369,21 +430,14 @@ }; | ||
} | ||
return decrypt_assertion(saml_response, _this.private_key, cb_wf); | ||
switch (false) { | ||
case saml_response.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:protocol', 'Response').length !== 1: | ||
response.type = 'authn_response'; | ||
return parse_authn_response(saml_response, _this.private_key, identity_provider.certificate, cb_wf); | ||
case saml_response.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:protocol', 'LogoutResponse').length !== 1: | ||
response.type = 'logout_response'; | ||
return setImmediate(cb_wf, null, {}); | ||
} | ||
}; | ||
})(this), function(result, cb_wf) { | ||
decrypted_assertion = (new xmldom.DOMParser()).parseFromString(result); | ||
return check_saml_signature(result, identity_provider.certificate, cb_wf); | ||
}, function(cb_wf) { | ||
return async.lift(get_name_id)(decrypted_assertion, cb_wf); | ||
}, function(name_id, cb_wf) { | ||
user.name_id = name_id; | ||
return async.lift(get_session_index)(decrypted_assertion, cb_wf); | ||
}, function(session_index, cb_wf) { | ||
user.session_index = session_index; | ||
return async.lift(parse_assertion_attributes)(decrypted_assertion, cb_wf); | ||
}, function(assertion_attributes, cb_wf) { | ||
user = _.extend(user, pretty_assertion_attributes(assertion_attributes)); | ||
user = _.extend(user, { | ||
attributes: assertion_attributes | ||
}); | ||
return cb_wf(null, user); | ||
_.extend(response, result); | ||
return cb_wf(null, response); | ||
} | ||
@@ -393,20 +447,20 @@ ], cb); | ||
ServiceProvider.prototype.create_logout_url = function(user, identity_provider, cb) { | ||
ServiceProvider.prototype.create_logout_url = function(identity_provider, name_id, session_index, cb) { | ||
var xml; | ||
xml = create_logout_request(this.issuer, user.name_id, user.session_index); | ||
return zlib.deflateRaw(xml, function(err, deflated) { | ||
var uri; | ||
if (err != null) { | ||
return cb(err); | ||
} | ||
uri = url.parse(identity_provider.sso_login_url); | ||
uri.query = { | ||
SAMLRequest: deflated.toString('base64') | ||
xml = create_logout_request(this.issuer, name_id, session_index, identity_provider.sso_logout_url); | ||
return zlib.deflateRaw(xml, (function(_this) { | ||
return function(err, deflated) { | ||
var uri; | ||
if (err != null) { | ||
return cb(err); | ||
} | ||
uri = url.parse(identity_provider.sso_logout_url); | ||
uri.query = sign_get_request(deflated.toString('base64'), _this.private_key); | ||
return cb(null, url.format(uri)); | ||
}; | ||
return cb(null, url.format(uri)); | ||
}); | ||
})(this)); | ||
}; | ||
ServiceProvider.prototype.create_metadata = function(identity_provider, assert_endpoint, cb) { | ||
return create_metadata(this.issuer, assert_endpoint, this.certificate, this.certificate, cb); | ||
ServiceProvider.prototype.create_metadata = function(assert_endpoint) { | ||
return create_metadata(this.issuer, assert_endpoint, this.certificate, this.certificate); | ||
}; | ||
@@ -432,2 +486,3 @@ | ||
module.exports.create_metadata = create_metadata; | ||
module.exports.sign_get_request = sign_get_request; | ||
module.exports.check_saml_signature = check_saml_signature; | ||
@@ -434,0 +489,0 @@ module.exports.check_status_success = check_status_success; |
{ | ||
"name": "saml2-js", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "SAML 2.0 node helpers", | ||
@@ -5,0 +5,0 @@ "author": "Clever", |
@@ -9,2 +9,8 @@ # SAML2 Library | ||
## Installation | ||
```bash | ||
npm install saml2-js | ||
``` | ||
## Expected Usage | ||
@@ -11,0 +17,0 @@ |
Sorry, the diff of this file is not supported yet
56899
451
103