Express Authentication

Authentication (and authorization?) with Express for RESTful APIs.

As usual with Node, too many existing solutions — and their documentation sucks.

Requirements

  1. Offer users all options: basic authentication (credentials), OAuth, maybe even OpenID?
  2. We're going to link/group profiles: multiple social profiles or accounts may access their "parent" profile.
  3. "Remember me" option
  4. SPA, REST, WebSockets, Express, Teacup…
  5. Debugging

Solutions?

  1. Passport seems popular, but documentation is, meh, verbose and convoluted: code snippets that just didn't work for me, no formalized API docs, etc. Harrowing experience.
  2. Everyauth seems inactive, abandoned.

API

Wait, just an overview of where all the pieces go…

Setup

  1. package.json:
    "dependencies":{
    	"express":"3.4.x",
    	"passport":"0.1.17",
    	"passport-local":"0.1.6"
    for the standard "npm install".
  2. var express=require('express'),
    	passport=require('passport');

Express middleware

  1. app
    	.use(passport.initialize())
    	.use(passport.session())
    but, note order is significant.

REST over HTTP

Single page application: serving some static assets, generic boilerplate stuff, that probably aren't access controlled, and the rest is RESTful APIs for the UI to use through AJAX (no HTML, only JSON — "data over the wire" ;o).

Striving to be stateless (REST), not so much for HTTP caching but for partitioninig, ie scalability, so?

Session?

HTTP basic auth or cookies? (Assuming HTTPS, of course.)

Don't need sessions (cookie based) with HTTP basic auth, because browser manages the session, so use:

passport.authenticate('basic',{session:false}).

Express (Connect?) sessions

passport.serializeUser(function(user, done) {
done(null, user.id);
});

passport.deserializeUser(function(id, done) {

Cookies

  1. Passport(?) sets (??? this is what the browser returns):
    Cookie: connect.sess=s%3Aj%3A%7B%22passport%22%3A%7B%7D%7D.nR%2FuoNl%2BbwC1%2B%2FlcD0sTLMJ7XCIGMOlDrUhXiEtLC0s

Strategy

  1. Requires a "verify callback":
    passport.use(new (require('passport-local').Strategy)(
    	function(username,password,done){
  2. This "verify callback" is only called (presumably by the middleware returned from the passport.authenticate() factory) when the request (either query string or body) contains username and password. The middleware, printed to console:
    function verified(err, user, info) {
    	if (err) { return self.error(err); }
    	if (!user) { return self.fail(info); }
    	self.success(user, info);
    }

Routes

Authenticate and authorize every request.

  1. app.get('/foo',
    	passport.authenticate('local'),
    	function(){
  2. Repeating the strategy ('local') seems excessive: why would I be mixing strategies in the same app?
  3. Once for all URLs matching, say, a prefix?
    app.get('/api/1/*',
    	passport.authenticate('local'));
    "If authentication fails, Passport will respond with a 401 Unauthorized status, and any additional route handlers will not be invoked."
    Same as:
    app.use('/api/1/',passport.authenticate('local'));
    Note the different URL! Doesn't use() do the same parsing/expansions as router?
  4. authenticate() can be called from the route handler, too, instead of as a middleware, but I don't understand the reason:
    "In this example, note that authenticate() is called from within the route handler, rather than being used as route middleware. This gives the callback access to the req and res objects through closure."
    Aren't middleware passed these arguments anyway? What's the difference, then?

Routing? Because typically authentication control flow uses redirections? But, I only need it for an API now?

localhost (for local development)?


--
The real world is a special case