Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Commit

Permalink
Add my first draft
Browse files Browse the repository at this point in the history
  • Loading branch information
Francois Marier committed Nov 11, 2012
1 parent 1a2e6bb commit 4858182
Showing 1 changed file with 124 additions and 0 deletions.
124 changes: 124 additions & 0 deletions client-sessions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# client-side sessions with Node.JS

Static websites are easy to scale. You can cache the heck out of them and
you don't have state to propagate between the various servers that deliver
this content to end-users.

Unfortunately, most web applications need to carry some state in order to
offer a personalized experience to users. If users can log into your site,
then you need to keep sessions for them. The typical way that this is done
is by setting a cookie with a random session identifier and storing session
details on the server under this identifier.

# Scaling a stateful service

Now, if you want to scale that service, you essentially have to:

1. replicate that session data across all of the web servers,
2. use a central store that each web server connects to, or
3. ensure that a given user always hits the same web server

These all have downsides:

* Replication has a performance cost and increases complexity.
* A central store will limit scaling and increase latency.
* Confining users to a specific server leads to problems when that
server needs to come down.

However, if you flip the problem around, you can find a fourth option: storing
the session data on the client.

# Client-side sessions

Pushing the session data to the browser has some obvious advantages:

1. the data is always available, regardless of which machine is serving a user
2. there is no state to lose on the server
3. new web servers can be added instantly
4. nothing needs to be replicated between the web servers

There is one key problem though: you cannot trust the client not to tamper
with the session data.

For example, if you store the user ID for the user's account in a cookie,
it would be easy for that user to change that ID and then gain access to
someone else's account.

While this sounds like a deal breaker, there is a clever solution to
work around this trust problem: store the session data in a
tamper-proof package. That way, there is no need to trust that the
user hasn't modified the session data. It can be verified by the
server.

What that means in practice is that you use [authenticated encryption](https://en.wikipedia.org/wiki/Authenticated_encryption) to
keep users from reading or modifying the session data and then you put that encrypted data inside the cookie. This is what [client-sessions](https://github.com/benadida/node-client-sessions) does.

# node-client-sessions

It's quite simple to get started with client-sessions because it was designed as a
[Connect](http://www.senchalabs.org/connect/) / [Express](http://expressjs.com/) middleware. It replaces connect's built-in [session](http://www.senchalabs.org/connect/session.html)
and [cookieParser](http://www.senchalabs.org/connect/cookieParser.html) middlewares.

This is how you can add it to a [simple Express application](https://github.com/fmarier/node-client-sessions-sample):

const clientSessions = require("client-sessions");

app.use(clientSessions({
secret: '0GBlJZ9EKBt2Zbi2flRPvztczCewBxXK' // set this to a long random string!
}));

Then, you can set properties on the `req.session` object like this:

app.get('/login', function (req, res){
req.session.username = 'JohnDoe';
});

and read them back:

app.get('/', function (req, res){
res.send('Welcome ' + req.session.username);
});

To terminate the session, use the reset function:

app.get('/logout', function (req, res) {
req.session.reset();
});

# gotchas

One of the main downsides of client-side sessions as compared to server-side
ones is that the server no longer has the ability to destroy sessions.

Using a server-side scheme, it's enough to delete the session data that's
stored on the server because any cookies that remain on clients will now
point to a non-existent session. With a client-side scheme though, the
session data is not on the server, so the server cannot be sure that it has
been deleted on every client.

To compensate for this limitation, client-sessions adds an expiry to the
cookies. Before unpacking the session data stored in the encrypted cookie,
the server will check that it hasn't expired. If it has, it will simply
refuse to honour it and consider the user as logged out.

# Immediate revocation of Persona sessions

While the expiry scheme works fine in most applications (especially when it's set to a
relatively low value), in the case of [Persona](https://login.persona.org),
we needed a way for users to immediately revoke their sessions as soon as they learn that they password has been compromised.

This meant keeping a little bit of state on the backend. The way we
[made this instant revocation possible](https://github.com/mozilla/browserid/commit/1b0444d85700a951edc74a0bf7ad5581b2cbfedd)
was by adding a new token in the user table as well as in
the session cookie.

Every API call that looks at the cookie now also reads the current token
value from the database and compares it with the token from the cookie. Unless they are the same, an error is returned and the user is logged out.

The downside of this solution, of course, is the extra database read for each
API call, but fortunately we already read from the user table in most of
these calls, so the new token can be pulled in at the same time.

# Learn more

If you want to give [client-sessions](https://github.com/benadida/node-client-sessions) a go, have a look at this [simple demo application](https://github.com/fmarier/node-client-sessions-sample). Then if you find any bugs, let us know via [our bug tracker](https://github.com/benadida/node-client-sessions/issues).

0 comments on commit 4858182

Please sign in to comment.