Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js Stateless/Cookieless Sessions API Authentication

I'm building an API and trying to figure out Authentication in a number of contexts.

Sessions and Password Authentication

The API needs to serve client-side applications we create and deploy, and handle authenticated requests using a password. It's not a great idea to send the password with every request, so it makes more sense to first hit a login endpoint and get a session id. The webapp in question is written in AngularJS, and should track its own session in localStorage to mitigate session hijacking and remove the dependency on cookies to track sessions.

The webapp would need to send the session identifier with every request, and currently does so in the body of the request. This fragments pretty easily and becomes tightly coupled with the API. I would much rather pass all authentication information in one way—through a header, preferably—instead of in a number of different fields spread across the request body, url and headers.

Session Storage

Redis is awesome, of course. Session storage is straightforward and automatically garbage-collects. Unfortunately, sessions in redis are difficult to manage: I cannot easily revoke all sessions for a given user. Adding that capability by storing sessions in a true redis-datastructure instead of the global keyspace removes the ability to add keyed TTLs. My current solution is to store a list of sessions in the MongoDB user collection, and garbage-collect expired sessions on session activity (login/logout, for instance).

The sessions are stored in redis with the connect-redis module, but as they are sent with every request aren't included in the cookies. At the moment, I have a small piece of middleware that takes the session identifier out of the request body and puts it into a req.cookies object.

var express = require('express');
var RedisStore = require('connect-redis')(require('connect'));

var app = express();
app.use(function(req, res, next) {
  req.cookies = {session: req.body.session};
});
app.use(express.session({
  store: new RedisStore({
    client: redisClient,
    prefix: 'session:'
  }),
  key: 'session',
  secret: 'all mine'
});

This approach works fine, except that express.session ends up setting the cookie when express responds, and that's not the desired behavior.

How do I set this up properly in the context of Express?

API Keys

Our API should also support API keys for third-party applications to gain limited and controlled access to our system. The most common mechanism I know of to do this is to distribute API keys to interested developers, and have the developer pass the API key in as part of the request. This runs into the same dilemma that session/password authentication runs into: every API expects the API key in a different part of the request, from the body to the url to the headers.

Expansion and Open Standards

While we do not plan on supporting Open authentication standards like OpenAuth and OpenID at initial release, we do wish to create a framework in which the addition of said standards is straightforward. Part of that could be to unify how authorization credentials are handed to the API, as with session/password and API Key backed authentication.

Another question asks whether customizing the HTTP Authorization header is a good idea, or if a custom header is a better idea.

CRUD Support

In order to support the CRUD paradigm for RESTful APIs, it makes sense to not provide authentication information in the body as that would limit all API requests to POST requests, whereas CRUD recommends use of a variety of HTTP methods.

TLDR

Two things:

  1. How can we use a module like connect-redis without using cookie-based sessions.
  2. How should we configure authentication information for maximum flexibility and interoperability?
like image 891
skeggse Avatar asked Oct 15 '13 18:10

skeggse


Video Answer


1 Answers

As given, the question is unanswerable. A session is state. Therefore you cannot implement sessions statelessly.

If you actually want stateless authentication, then you can't have sessions, and you should use the HTTP Authentication mechanism.

If you actually want sessions, but you don't want to transmit a state token in the Cookie header, then you should use an OAuth mechanism, which allows use of the Authorization header or a request parameter to hold the negotiated state token.

we do wish to create a framework in which the addition of said standards is straightforward

The easiest and best way to do that is to use them to start with. Don't re-invent the wheel. OAuth2 is designed to be easily implemented in multiple use-cases and has its own extension mechanism should you need more.

like image 139
OrangeDog Avatar answered Sep 20 '22 05:09

OrangeDog