Comparing version 0.1.1 to 0.1.2
299
openid.js
@@ -0,1 +1,3 @@ | ||
/* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||
/* vim: set sw=2 ts=2 et tw=80 : */ | ||
/* OpenID for node.js | ||
@@ -33,2 +35,3 @@ * | ||
http = require('http'), | ||
https = require('https'), | ||
querystring = require('querystring'), | ||
@@ -81,3 +84,6 @@ url = require('url'), | ||
{ | ||
_associations[handle] = { type : type, secret: secret, expiry_time: expiry_time}; | ||
setTimeout(function() { | ||
openid.removeAssociation(handle); | ||
}, expiry_time); | ||
_associations[handle] = {type : type, secret: secret}; | ||
} | ||
@@ -95,5 +101,11 @@ | ||
openid.removeAssociation = function(handle) | ||
{ | ||
delete _associations[handle]; | ||
return true; | ||
} | ||
function _buildUrl(theUrl, params) | ||
{ | ||
theUrl = url.parse(theUrl, true); | ||
theUrl = url.parse(theUrl); | ||
if(params) | ||
@@ -107,2 +119,3 @@ { | ||
{ | ||
theUrl.query = querystring.parse(theUrl.query); | ||
for(var key in params) | ||
@@ -121,29 +134,19 @@ { | ||
function _get(getUrl, params, callback) | ||
function _get(getUrl, params, callback, redirects) | ||
{ | ||
getUrl = url.parse(_buildUrl(getUrl, params), true); | ||
redirects = redirects || 5; | ||
getUrl = url.parse(_buildUrl(getUrl, params)); | ||
var path = getUrl.pathname; | ||
if(!path) | ||
{ | ||
path = '/'; | ||
} | ||
var path = getUrl.pathname || '/'; | ||
if(getUrl.query) | ||
{ | ||
path += '?' + querystring.stringify(getUrl.query) | ||
path += '?' + getUrl.query; | ||
} | ||
var client = http.createClient( | ||
_isDef(getUrl.port) | ||
? getUrl.port | ||
: (getUrl.protocol == 'https:' | ||
? 443 | ||
: 80), | ||
getUrl.hostname, | ||
getUrl.protocol == 'https:'); | ||
var req = client.request('GET', path, { 'Host': getUrl.hostname }); | ||
req.end(); | ||
req.on('response', function(res) | ||
var options = { | ||
host: getUrl.hostname, | ||
port: _isDef(getUrl.port) ? getUrl.port : | ||
(getUrl.protocol == 'https:' ? 443 : 80), | ||
path: path | ||
}; | ||
(getUrl.protocol == 'https:' ? https : http).get(options, function(res) | ||
{ | ||
@@ -158,34 +161,38 @@ var data = ''; | ||
{ | ||
callback(data, res.headers, res.statusCode); | ||
if(res.headers.location && --redirects) | ||
{ | ||
_get(res.headers.location, params, callback, redirects); | ||
} | ||
else | ||
{ | ||
callback(data, res.headers, res.statusCode); | ||
} | ||
}); | ||
}).on("error", function () { | ||
callback(); | ||
}); | ||
} | ||
function _post(getUrl, data, callback) | ||
function _post(getUrl, data, callback, redirects) | ||
{ | ||
getUrl = url.parse(getUrl, true); | ||
redirects = redirects || 5; | ||
getUrl = url.parse(getUrl); | ||
var client = http.createClient( | ||
_isDef(getUrl.port) | ||
? getUrl.port | ||
: (getUrl.protocol == 'https:' | ||
? 443 | ||
: 80), | ||
getUrl.hostname, | ||
getUrl.protocol == 'https:'); | ||
var path = getUrl.pathname; | ||
if(!path) | ||
{ | ||
path = '/'; | ||
} | ||
var path = getUrl.pathname || '/'; | ||
if(getUrl.query) | ||
{ | ||
path += '?' + querystring.stringify(getUrl.query) | ||
path += '?' + getUrl.query; | ||
} | ||
var encodedData = _encodePostData(data); | ||
var req = client.request('POST', path, { 'Host' : getUrl.hostname, 'Content-Type': | ||
'application/x-www-form-urlencoded', 'Content-Length': encodedData.length }); | ||
req.end(encodedData); | ||
req.on('response', function(res) | ||
var options = { | ||
host: getUrl.hostname, | ||
port: _isDef(getUrl.port) ? getUrl.port : | ||
(getUrl.protocol == 'https:' ? 443 : 80), | ||
path: path, | ||
headers: {'Content-Type': 'application/x-www-form-urlencoded', | ||
'Content-Length': encodedData.length}, | ||
method: 'POST' | ||
}; | ||
(getUrl.protocol == 'https:' ? https : http).request(options, function(res) | ||
{ | ||
@@ -200,5 +207,14 @@ var data = ''; | ||
{ | ||
callback(data, res.headers, res.statusCode); | ||
if(res.headers.location && --redirects) | ||
{ | ||
_post(res.headers.location, params, callback, redirects); | ||
} | ||
else | ||
{ | ||
callback(data, res.headers, res.statusCode); | ||
} | ||
}); | ||
}); | ||
}).on("error", function () { | ||
callback(); | ||
}).end(encodedData); | ||
} | ||
@@ -235,2 +251,4 @@ | ||
identifier = identifier.replace(/^\s+|\s+$/g, ''); | ||
if(!identifier) | ||
return null; | ||
if(identifier.indexOf('xri://') === 0) | ||
@@ -455,2 +473,6 @@ { | ||
identifier = _normalizeIdentifier(identifier); | ||
if(!identifier) { | ||
callback(null); | ||
return; | ||
} | ||
if(identifier.indexOf('http') !== 0) | ||
@@ -581,4 +603,4 @@ { | ||
openid.saveAssociation(hashAlgorithm, | ||
data.assoc_handle, secret, new Date().getTime() + data.expires_in); | ||
openid.saveAssociation(hashAlgorithm, | ||
data.assoc_handle, secret, data.expires_in * 1); | ||
@@ -636,3 +658,3 @@ callback(data); | ||
openid.authenticate = function(identifier, returnUrl, realm, immediate, callback) | ||
openid.authenticate = function(identifier, returnUrl, realm, immediate, callback, extensions) | ||
{ | ||
@@ -643,3 +665,3 @@ openid.discover(identifier, function(providers, version) | ||
{ | ||
throw new Error("No provider discovered for identity"); | ||
callback(null, "No provider discovered for identity"); | ||
} | ||
@@ -655,6 +677,6 @@ | ||
// TODO: Do dumb/stateless mode | ||
return console.log(answer); | ||
callback(null, answer.error); | ||
} | ||
_requestAuthentication(provider, answer.assoc_handle, returnUrl, realm, immediate, callback); | ||
_requestAuthentication(provider, answer.assoc_handle, returnUrl, realm, immediate, callback, extensions || {}); | ||
}); | ||
@@ -665,3 +687,3 @@ } | ||
function _requestAuthentication(provider, assoc_handle, returnUrl, realm, immediate, callback) | ||
function _requestAuthentication(provider, assoc_handle, returnUrl, realm, immediate, callback, extensions) | ||
{ | ||
@@ -677,2 +699,7 @@ var params = { | ||
for(var i in extensions) { | ||
for(var key in extensions[i].requestParams) | ||
params[key] = extensions[i].requestParams[key]; | ||
} | ||
// TODO: 1.1 compatibility | ||
@@ -733,5 +760,7 @@ if(provider.claimedIdentifier) | ||
assertionUrl = url.parse(assertionUrl, true); | ||
assertionUrl = url.parse(assertionUrl); | ||
assertionUrl.query = querystring.parse(assertionUrl.query); | ||
var params = _fixParams(assertionUrl.query); | ||
var assertionError = _getAssertionError(assertionUrl.query); | ||
var assertionError = _getAssertionError(params); | ||
if(assertionError) | ||
@@ -741,3 +770,3 @@ { | ||
} | ||
if(!_checkValidHandle(assertionUrl.query)) | ||
if(!_checkValidHandle(params)) | ||
{ | ||
@@ -747,3 +776,3 @@ return { authenticated: false, error: 'Association handle has been invalidated' }; | ||
if(!_checkSignature(assertionUrl.query)) | ||
if(!_checkSignature(params)) | ||
{ | ||
@@ -753,3 +782,6 @@ return { authenticated: false, error: 'Provider signature is invalid or expired' }; | ||
return { authenticated : true , identifier: _param(assertionUrl.query, 'openid.claimed_id') }; | ||
// make sure to remove already used associations to prevent replay | ||
openid.removeAssociation(params['openid.assoc_handle']); | ||
return { authenticated : true , identifier: params['openid.claimed_id'], | ||
params: params }; | ||
} | ||
@@ -763,7 +795,7 @@ | ||
} | ||
else if(_param(params, 'openid.mode') == 'error') | ||
else if(params['openid.mode'] == 'error') | ||
{ | ||
return _param(params, 'openid.error'); | ||
return params['openid.error']; | ||
} | ||
else if(_param(params, 'openid.mode') == 'cancel') | ||
else if(params['openid.mode'] == 'cancel') | ||
{ | ||
@@ -778,3 +810,3 @@ return 'Authentication cancelled'; | ||
{ | ||
return !_isDef(_param(params, 'openid.invalidate_handle')); | ||
return !_isDef(params['openid.invalidate_handle']); | ||
} | ||
@@ -784,4 +816,4 @@ | ||
{ | ||
if(!_isDef(_param(params, 'openid.signed')) || | ||
!_isDef(_param(params, 'openid.sig'))) | ||
if(!_isDef(params['openid.signed']) || | ||
!_isDef(params['openid.sig'])) | ||
{ | ||
@@ -791,4 +823,4 @@ return false; | ||
var association = openid.loadAssociation(_param(params, 'openid.assoc_handle')); | ||
if(association.expiry_time < new Date().getTime()) | ||
var association = openid.loadAssociation(params['openid.assoc_handle']); | ||
if(!association) | ||
{ | ||
@@ -799,7 +831,7 @@ return false; | ||
var message = ''; | ||
var signedParams = _param(params, 'openid.signed').split(','); | ||
var signedParams = params['openid.signed'].split(','); | ||
for(var index in signedParams) | ||
{ | ||
var param = signedParams[index]; | ||
var value = _param(params, 'openid.' + param); | ||
var value = params['openid.' + param]; | ||
if(!_isDef(value)) | ||
@@ -816,14 +848,127 @@ { | ||
return ourSignature == _param(params, 'openid.sig'); | ||
return ourSignature == params['openid.sig']; | ||
} | ||
// Recursive parameter lookup for node v0.2.x | ||
function _param(params, key) { | ||
if (!params[key] && process.version.match(/^v0\.2\./)) { | ||
var parts = key.split('.'); | ||
var first = parts.shift(); | ||
return params[first] ? _param(params[first], parts.join('.')) : undefined; | ||
function _fixParams(params) { | ||
for (var key in params) { | ||
if (typeof(params[key]) == "object") { | ||
_fixParams(params[key]); | ||
for (var childkey in params[key]) { | ||
params[key + "." + childkey] = params[key][childkey]; | ||
} | ||
delete params[key]; | ||
} | ||
} | ||
return params; | ||
} | ||
return params[key]; | ||
/* ----- Extensions ----- */ | ||
function _getExtensionAlias(params, ns) { | ||
for (var k in params) | ||
if (params[k] == ns) | ||
return k.replace("openid.ns.", ""); | ||
} | ||
/* Simple Registration Extension: http://openid.net/specs/openid-simple-registration-extension-1_1-01.html */ | ||
var sreg_keys = ["nickname", "email", "fullname", "dob", "gender", "postcode", "country", "language", "timezone"]; | ||
openid.SimpleRegistration = function SimpleRegistration(options) { | ||
if (options.params) { // parsing the successful result | ||
var extension = _getExtensionAlias(options.params, "http://openid.net/extensions/sreg/1.1") || "sreg"; | ||
for (var i in sreg_keys) { | ||
var key = sreg_keys[i]; | ||
if (options.params["openid." + extension + "." + key]) | ||
this[key] = options.params["openid." + extension + "." + key]; | ||
} | ||
} else { // constructing the params for the auth request | ||
this.requestParams = {"openid.ns.sreg": "http://openid.net/extensions/sreg/1.1"}; | ||
if (options.policy_url) | ||
this.requestParams["openid.sreg.policy_url"] = options.policy_url; | ||
var required = []; | ||
var optional = []; | ||
for (var i in sreg_keys) { | ||
var key = sreg_keys[i]; | ||
if (options[key]) { | ||
if (options[key] == "required") | ||
required.push(key); | ||
else | ||
optional.push(key); | ||
} | ||
if (required.length) | ||
this.requestParams["openid.sreg.required"] = required.join(","); | ||
if (optional.length) | ||
this.requestParams["openid.sreg.optional"] = optional.join(","); | ||
} | ||
} | ||
} | ||
/* User Interface Extension: http://svn.openid.net/repos/specifications/user_interface/1.0/trunk/openid-user-interface-extension-1_0.html */ | ||
openid.UserInterface = function UserInterface(options) { | ||
if (typeof(options) != "object") | ||
options = {mode: options || "popup"}; | ||
this.requestParams = {"openid.ns.ui": "http://specs.openid.net/extensions/ui/1.0"}; | ||
for (var k in options) { | ||
this.requestParams["openid.ui." + k] = options[k]; | ||
} | ||
} | ||
/* Attribute Exchange Extension: http://openid.net/specs/openid-attribute-exchange-1_0.html */ | ||
/* also see http://www.axschema.org/types/ and http://code.google.com/intl/de-DE/apis/accounts/docs/OpenID.html#Parameters */ | ||
// TODO: count handling | ||
var attributeMapping = { | ||
"http://axschema.org/contact/country/home": "country" | ||
, "http://axschema.org/contact/email": "email" | ||
, "http://axschema.org/namePerson/first": "firstname" | ||
, "http://axschema.org/pref/language": "language" | ||
, "http://axschema.org/namePerson/last": "lastname" | ||
// the following are not in the google document: | ||
, "http://axschema.org/namePerson/friendly": "nickname" | ||
, "http://axschema.org/namePerson": "fullname" | ||
}; | ||
openid.AttributeExchange = function AttributeExchange(options) { | ||
if (options.params) { // parsing the successful result | ||
var extension = _getExtensionAlias(options.params, "http://openid.net/srv/ax/1.0") || "ax"; | ||
var regex = new RegExp("^openid\\." + extension + "\\.(value|type)\\.(\\w+)$"); | ||
var aliases = {}; | ||
var values = {}; | ||
for (var k in options.params) { | ||
var matches = k.match(regex); | ||
if (!matches) | ||
continue; | ||
if (matches[1] == "type") | ||
aliases[options.params[k]] = matches[2]; | ||
else | ||
values[matches[2]] = options.params[k]; | ||
} | ||
for (var ns in aliases) { | ||
if (aliases[ns] in values) | ||
this[ns] = values[aliases[ns]]; | ||
} | ||
} else { // constructing the params for the auth request | ||
this.requestParams = {"openid.ns.ax": "http://openid.net/srv/ax/1.0", | ||
"openid.ax.mode" : "fetch_request"}; | ||
var required = []; | ||
var optional = []; | ||
for (var ns in options) { | ||
if (options[ns] == "required") | ||
required.push(ns); | ||
else | ||
optional.push(ns); | ||
} | ||
var self = this; | ||
required = required.map(function (ns, i) { | ||
var attr = attributeMapping[ns] || "req" + i; | ||
self.requestParams["openid.ax.type." + attr] = ns; | ||
return attr; | ||
}); | ||
optional = optional.map(function (ns, i) { | ||
var attr = attributeMapping[ns] || "opt" + i; | ||
self.requestParams["openid.ax.type." + attr] = ns; | ||
return attr; | ||
}); | ||
if (required.length) | ||
this.requestParams["openid.ax.required"] = required.join(","); | ||
if (optional.length) | ||
this.requestParams["openid.ax.if_available"] = optional.join(","); | ||
} | ||
} |
@@ -7,3 +7,3 @@ { name: 'openid', | ||
'web': 'http://ox.no' }], | ||
version: '0.1.1', | ||
version: '0.1.2', | ||
repository: { | ||
@@ -18,3 +18,3 @@ 'type': 'git', | ||
'lib' : './lib' }, | ||
'main' : './openid.js' | ||
'main' : './openid.js', | ||
} |
@@ -31,6 +31,7 @@ # OpenID for node.js | ||
var url = require('url'); | ||
var querystring = require('querystring'); | ||
var server = require('http').createServer( | ||
function(req, res) | ||
{ | ||
var parsedUrl = url.parse(req.url, true); | ||
var parsedUrl = url.parse(req.url); | ||
if(parsedUrl.pathname == '/verify') | ||
@@ -40,4 +41,11 @@ { | ||
var result = openid.verifyAssertion(req); // or req.url | ||
var attributes = []; | ||
var sreg = new openid.SimpleRegistration(result); | ||
for (var k in sreg) | ||
attributes.push(k + ": " + sreg[k]); | ||
var ax = new openid.AttributeExchange(result); | ||
for (var k in ax) | ||
attributes.push(k + ": " + ax[k]); | ||
res.writeHead(200); | ||
res.end(result.authenticated ? 'Success :)' : 'Failure :('); | ||
res.end(result.authenticated ? 'Success :)\n' + attributes.join("\n") : 'Failure :(\n' + result.error); | ||
} | ||
@@ -48,3 +56,3 @@ else if(parsedUrl.pathname == '/authenticate') | ||
openid.authenticate( | ||
parsedUrl.query.openid_identifier, // user supplied identifier | ||
querystring.parse(parsedUrl.query).openid_identifier, // user supplied identifier | ||
'http://example.com/verify', // our callback URL | ||
@@ -57,3 +65,10 @@ null, // realm (optional) | ||
res.end(); | ||
}); | ||
}, [new openid.UserInterface(), new openid.SimpleRegistration({ | ||
"nickname" : true, "email" : true, "fullname" : true, | ||
"dob" : true, "gender" : true, "postcode" : true, | ||
"country" : true, "language" : true, "timezone" : true}), | ||
new openid.AttributeExchange({ | ||
"http://axschema.org/contact/email": "required", | ||
"http://axschema.org/namePerson/friendly": "required", | ||
"http://axschema.org/namePerson": "required"})]); | ||
} | ||
@@ -60,0 +75,0 @@ else |
@@ -30,6 +30,7 @@ /* A simple sample demonstrating OpenID for node.js | ||
var url = require('url'); | ||
var querystring = require('querystring'); | ||
var server = require('http').createServer( | ||
function(req, res) | ||
{ | ||
var parsedUrl = url.parse(req.url, true); | ||
var parsedUrl = url.parse(req.url); | ||
if(parsedUrl.pathname == '/verify') | ||
@@ -39,4 +40,11 @@ { | ||
var result = openid.verifyAssertion(req); // or req.url | ||
var attributes = []; | ||
var sreg = new openid.SimpleRegistration(result); | ||
for (var k in sreg) | ||
attributes.push(k + ": " + sreg[k]); | ||
var ax = new openid.AttributeExchange(result); | ||
for (var k in ax) | ||
attributes.push(k + ": " + ax[k]); | ||
res.writeHead(200); | ||
res.end(result.authenticated ? 'Success :)' : 'Failure :('); | ||
res.end(result.authenticated ? 'Success :)\n' + attributes.join("\n") : 'Failure :(\n' + result.error); | ||
} | ||
@@ -47,11 +55,24 @@ else if(parsedUrl.pathname == '/authenticate') | ||
openid.authenticate( | ||
parsedUrl.query.openid_identifier, // user supplied identifier | ||
querystring.parse(parsedUrl.query).openid_identifier, // user supplied identifier | ||
'http://example.com/verify', // our callback URL | ||
null, // realm (optional) | ||
false, // attempt immediate authentication first? | ||
function(authUrl) | ||
function(authUrl, error) | ||
{ | ||
if (error) | ||
{ | ||
res.writeHead(200); | ||
res.end(error); | ||
return; | ||
} | ||
res.writeHead(302, { Location: authUrl }); | ||
res.end(); | ||
}); | ||
}, [new openid.UserInterface(), new openid.SimpleRegistration({ | ||
"nickname" : true, "email" : true, "fullname" : true, | ||
"dob" : true, "gender" : true, "postcode" : true, | ||
"country" : true, "language" : true, "timezone" : true}), | ||
new openid.AttributeExchange({ | ||
"http://axschema.org/contact/email": "required", | ||
"http://axschema.org/namePerson/friendly": "required", | ||
"http://axschema.org/namePerson": "required"})]); | ||
} | ||
@@ -58,0 +79,0 @@ else |
@@ -30,2 +30,23 @@ /* OpenID for node.js | ||
exports.testResolveFailed = function(test) | ||
{ | ||
openid.authenticate('example.com', 'http://example.com/verify', null, false, | ||
function(data, error) | ||
{ | ||
assert.equal(null, data); | ||
assert.ok(error); | ||
test.done(); | ||
}); | ||
} | ||
exports.testEmptyUrl = function(test) | ||
{ | ||
openid.discover('', | ||
function(data) | ||
{ | ||
assert.equal(null, data); | ||
test.done(); | ||
}); | ||
} | ||
exports.testResolveRyanXri = function(test) | ||
@@ -41,2 +62,12 @@ { | ||
exports.testResolveRedirect = function(test) | ||
{ | ||
openid.discover('http://www.myopenid.com/xrds?username=swatinem.myopenid.com', | ||
function(data) | ||
{ | ||
assert.equal(3, data.length); | ||
test.done(); | ||
}); | ||
} | ||
exports.testResolveGoogle = function(test) | ||
@@ -43,0 +74,0 @@ { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
91582
2646
103
3