09 January, 2011

OpenID for node.js

I am happy to announce the immediate availability of OpenID for Node.js. I've spent a few nights working on this, which is my first Node.js library, and I finally got to the point where the library was complete enough for a first public release.

Installation

Installing is very simple using npm, just do npm install openid.

If you don't use npm, you can of course just download and require(). Remember dependencies from the lib folder, and remember adding paths to require.paths if necessary.

Usage

One of the main design goals for the library is simplicity. Using OpenID for Node.js is pretty simple, here's a minimal sample:
/* A simple sample demonstrating OpenID for node.js
*
* http://ox.no/software/node-openid
* http://github.com/havard/node-openid
*
* Copyright (C) 2010 by Håvard Stranden
*
* 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
*/
var openid = require('./openid');
var url = require('url');
var querystring = require('querystring');
var extensions = [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"
}),
new openid.PAPE(
{
"max_auth_age": 24 * 60 * 60, // one day
"preferred_auth_policies" : "none" //no auth method preferred.
})];
var relyingParty = new openid.RelyingParty(
'http://example.com/verify', // Verification URL (yours)
null, // Realm (optional, specifies realm for OpenID authentication)
false, // Use stateless verification
false, // Strict mode
extensions); // List of extensions to enable and include
var server = require('http').createServer(
function(req, res)
{
var parsedUrl = url.parse(req.url);
if(parsedUrl.pathname == '/authenticate')
{
// User supplied identifier
var query = querystring.parse(parsedUrl.query);
var identifier = query.openid_identifier;
// Resolve identifier, associate, and build authentication URL
relyingParty.authenticate(identifier, false, function(error, authUrl)
{
if(error)
{
res.writeHead(200, { 'Content-Type' : 'text/plain; charset=utf-8' });
res.end('Authentication failed: ' + error.message);
}
else if (!authUrl)
{
res.writeHead(200, { 'Content-Type' : 'text/plain; charset=utf-8' });
res.end('Authentication failed');
}
else
{
res.writeHead(302, { Location: authUrl });
res.end();
}
});
}
else if(parsedUrl.pathname == '/verify')
{
// Verify identity assertion
// NOTE: Passing just the URL is also possible
relyingParty.verifyAssertion(req, function(error, result)
{
res.writeHead(200, { 'Content-Type' : 'text/plain; charset=utf-8' });
if(error)
{
res.end('Authentication failed: ' + error.message);
}
else
{
// Result contains properties:
// - authenticated (true/false)
// - answers from any extensions (e.g.
// "http://axschema.org/contact/email" if requested
// and present at provider)
res.end((result.authenticated ? 'Success :)' : 'Failure :(') +
'\n\n' + JSON.stringify(result));
}
});
}
else
{
// Deliver an OpenID form on all other URLs
res.writeHead(200, { 'Content-Type' : 'text/html; charset=utf-8' });
res.end('<!DOCTYPE html><html><body>'
+ '<form method="get" action="/authenticate">'
+ '<p>Login using OpenID</p>'
+ '<input name="openid_identifier" />'
+ '<input type="submit" value="Login" />'
+ '</form></body></html>');
}
});
server.listen(80);

Source

I use GitHub for source control, so you can follow me and the node-openid repository there.

Licensing

OpenID for Node.js is licensed under the MIT license. Details can be found in the LICENSE file on GitHub. The library includes third-party code released under the MIT and BSD licenses, see README for details.

Wishes for node.js

I've come across a couple of missing features which I'd love to see in node.js in the near future:


  • The crypto module should support Diffie-Hellman key exchange (OpenSSL supports it, so please provide bindings)
  • A big integer library should be available alongside the crypto module, and preferably seamlessly integrated with the crypto module - perhaps bindings to the GMP library could suffice?
  • More seamless unit testing libraries would be good

In addition, I would love to see a tailored IDE for Node.js applications.

2 comments:

  1. [...] This post was mentioned on Twitter by Håvard Stranden and Axel Nennker. Axel Nennker said: openid for node.js http://ox.no/posts/openid-for-node-js [...]

    ReplyDelete