It is a truth universally acknowledged that some kind of stateful sessions are indispensable to any website with users. I’m familiar with the Node/Express ecosystem, so I’ll write specifics about that. The theory and debates underlying session architecture should transfer to other languages.
- What is State?
- Serverful Sessions
- HTTP-Only Cookies as Primary Keys
- What goes in a session object?
- Server-Side Session Stores
- Serverless Sessions and JSON Web Tokens
- The Great Debate: Cookies vs. Tokens
What is State?
So, are websites stateful? The answer is, not stateful enough for a login-protected user system.
Server-side websites use data and algorithms to transform inputs into outputs. They, too, (at least Node.js) only have enough state for a single request-response cycle.
Sessions are not a built-in feature of Express; they require configuring the
express-session middleware on your express server instance (application-level middleware). More about middleware. We’ll take a crack at that once we have a fuller picture of what we’re doing.
HTTP-Only Cookies as Primary Keys
If you’ve worked with SQL databases at all, you’re familiar with the concept of primary keys. If you’re not familiar, a primary key is a number or string attached to some data that allows you to pick that entry out and identify it in another context.
Modern, HTTP-only cookies are the (encrypted) primary keys of sessions. Each Express session has a random id called the
sid. When the Express session middleware is configured, the browser makes its first query to the server. The server responds with whatever you
res.render and also sends a
set-cookie response header with the encrypted
sid to the browser. Your server’s response renders in the browser with whatever information you sent. But when it requests the next resource from your server, it sends back the
sid in a
cookie request header. If the server receives a
cookie request header from the browser, it doesn’t send the
set-header response header back. Read more about HTTP headers. That
cookie header tells the server that the client belongs to the session with the same
sid, so the server can pull up that user’s info from its session data.
What goes in a session object?
Never store passwords, hashes, credit card data, or anything else obviously sensitive in a session object. What should you store in the session object? Anything non-sensitive that helps you identify the user and their attributes on the client or the server across a series of requests. This will be things like associated database keys or _ids, names, emails, what they had for lunch, etc. Sensitive info can be pulled from the database or microservice fresh on demand using keys/_ids and/or environment variables. Since you don’t actually send the session object to the client, but just the cookie, any session info that you want to send to the client has to be send in the response itself; the cookie is just an identifier to connect the client browser with the server session object.
Server-Side Session Stores
Great! Express can keep track of who makes what request, and you can carry tidbits of info around the server with you for that person. But what if you are running a cluster of machines and one goes down? What if a different instance of the server receives the second request from a client?
Session stores to the rescue! The session store is a super simple table/collection/hashmap in virtually any type of database that saves each session object. When the client sends a
cookie header to the server, if the session is not in the server’s working memory, it checks the session store for the de-encrypted
sid. It typically only has a couple columns/document keys – one for the
sid, and one for a (possibly stringified) JSON-like object that is the session object. Express session stores are available for MySQL, MongoDB, Redis, and more — check them out at the express-session github page.
Below is a snippet from an express server configuration.
Serverless Sessions and JSON Web Tokens
Used to be, cookies were the only way. With the rise of serverless architectures, “server” functions are even more stateless because there is no there there. Nothing connects the requests except the client device making them. JSON web tokens (JWT) have become increasingly popular because they store session info on the client. JWT are sent to the client on authentication, often from a SaaS like Auth0, and stored in the browser (or on the phone) actually containing the data of the session within the token itself. Obviously they are encrypted. The client sends the token to the various serverless API endpoints to identify itself and persist the session for the client and the “server”. JWT have headers, a payload, and a cryptographic signature. Once you’ve verified them on the client side, you can store them in local storage or an old-school cookie, although they can be quite large and old-school cookies have a size limit. JWT also expire just like server-side sessions can.
The Great Debate: Cookies vs. Tokens
I’m not a security expert, but my personal preference is to use server-side sessions with a store and HTTP-only cookies if I actually have a server, and JWT if I don’t. Storing session data on the client feels ookie to me unless I absolutely have to. There is no definitive answer as of this writing whether cookies or tokens are more secure. Developers have strong feels for and against both. Google around for more opinions.
Any cookie or token should ALWAYS be sent over HTTPS.
- Express Session Docs
- Robert Hafner for Treehouse on Secure Cookies (2009)
- Robert Hafner for Treehouse on Secure Sessions (2009)
- ExpressJS Book on Sessions (2013)
- Jonathan Kresner Express Sessions Deep Dive (2015)
- Decembersoft on Authenticating a Session Cookie in Express with JWTs (2017?)
- Introduction to JWTs (2017)
- Auth0 (authentication as a service with JWTs)