Edit Page

How sessions work in Sails (advanced)

For our purposes, sessions are defined to be a few components that together allow you to store information about a user agent between requests.

A user agent is the software (browser or native application) that represents you on a device (e.g. a browser tab on your computer, a smartphone application, or your refrigerator). It is associated one-to-one with a cookie or access token.

Sessions can be very useful because the request/response cycle is stateless. The request/response cycle is considered stateless because neither the client nor the server inherently stores any information between different requests about a particular request. Therefore, the lifecycle of a request/response ends when a response is made to the requesting user agent (e.g. res.send()).

Note: we’re going to discuss sessions in the context of a browser user agent. While you can use sessions in Sails for whatever you like, it is generally a best practice to use them purely for storing the state of user agent authentication. Authentication is a process that allows a user agent to prove that they have a certain identity. For example, in order to access some protected functionality, I might need to prove that my browser tab actually corresponds with a particular user record in a database. If I provide you with a unique name and a password, you can look up the name and compare my password with a stored (hopefully encrypted) password. If there's a match, I'm authenticated. But how do you store that "authenticated-ness" between requests? That's where sessions come in.

What sessions are made of

#

There are three main components to the implementation of sessions in Sails:

  1. the session store where information is retained
  2. the middleware that manages the session
  3. a cookie that is sent along with every request and stores a session id (by default, sails.sid)

The session store can either be in memory (this is the default Sails session store) or in a database (Sails has built-in support for using Redis for this purpose). Sails builds on top of Connect middleware to manage the session, which includes using a cookie to store a session id (sid) on the user agent.

A day in the life of a request, a response, and a session

#

When a request is sent to Sails, the request header is parsed by the session middleware.

#

If the header does not contain a cookie, a sid is created in the session and a default session dictionary is added to req (e.g. req.session). At this point you can make changes to the session property (usually in a controller/action). For example, let's look at the following login action:

module.exports = {

  login: function(req, res) {

    // Authentication code here

    // If successfully authenticated

    req.session.userId = foundUser.id;   // returned from a database

    return res.json(foundUser);

  }
}

Here we added a userId property to req.session.

Note: the property will not be stored in the session store, nor will it be available to other requests until the response is sent.

Once the response is sent, any new requests will have access to req.session.userId. Since we didn't have a cookie in the request header, a cookie will be established for us.

#

Now when the user agent makes the next request, the Sails.sid stored on the cookie is checked for authenticity. If it matches an existing sid in the session store, the contents of the session store are added as a property on the req dictionary (req.session). We can access properties on req.session (e.g. req.session.userId) or set properties on it (e.g. req.session.userId == someValue). The values in the session store might change, but the Sails.sid and sid generally do not.

When does the Sails.sid change?

#

During development, the Sails session store is in memory. Therefore, when you close the Sails server, the current session store disappears. When Sails is restarted, although a user agent request contains a Sails.sid in the cookie, the sid is no longer in the session store. Therefore, a new sid will be generated and replaced in the cookie. The Sails.sid will also change if the user agent cookie expires or is removed.

The lifespan of a Sails cookie can be changed from its default setting (never expires) to a new setting by accessing the cookie.maxAge property in projectName/config/session.js.

Using Redis as the session store

#

Redis is a key-value database package that can be used as a session store that is separate from the Sails instance. This configuration for sessions has two benefits. The first is that the session store will remain viable between Sails restarts. The second is that if you have multiple Sails instances behind a load balancer, all of the instances can point to a single consolidated session store.

Enabling Redis session store in development

#

To enable Redis as your session store in development, first make sure you have a local Redis instance running on your machine (redis-server). Then, lift your app with sails lift --redis.

This is just a shortcut for sails lift --session.adapter=@sailshq/connect-redis --sockets.adapter=@sailshq/socket.io-redis. These packages are included as dependencies of new Sails apps by default, but if you're working with an upgraded app you'll need to npm install @sailshq/connect-redis and npm install @sailshq/socket.io-redis.

Note that this built-in configuration uses your local Redis instance. For advanced session configuration options, see Reference > Configuration > sails.config.session.

#

The value for the cookie is created by first hashing the sid with a configurable secret which is just a long string.

You can change the session secret property in projectName/config/session.js.

The Sails sid (e.g. Sails.sid) then becomes a combination of the plain sid followed by a hash of the sid plus the secret. To take this out of the world of abstraction, let's use an example. Sails creates a sid of 234lj232hg234jluy32UUYUHH and a session secret of 9238cca11a83d473e10981c49c4f. These values are simply two strings that Sails combines and hashes to create a signature of AuSosBAbL9t3Ev44EofZtIpiMuV7fB2oi. So the Sails.sid becomes 234lj232hg234jluy32UUYUHH.AuSosBAbL9t3Ev44EofZtIpiMuV7fB2oi and is stored in the user agent cookie by sending a set-cookie property in the response header.

What does this prevent? This prevents a user from guessing the sid. It also prevents a evildoer from spoofing a user into making an authetication request with a sid that the evildoer knows. This could allow the evildoer to use the sid to do bad things while the user is authenticated via the session.

Disabling sessions

#

Even if your Sails app is designed to be accessed by non-browser clients, such as toasters, you are strongly encouraged to use sessions for authentication. While it can sometimes be complex to understand, the built-in session mechanism in Sails (session store + HTTP-only cookies) is a tried and true solution that is generally less brittle, easier to use, and lower-risk than rolling out something yourself.

That said, sessions may not always be an option (for example, if you must integrate with a different authentication scheme like JWT). In these cases, you can disable sessions on an app-wide or per-request basis.

Disabling sessions for your entire app
#

To entirely turn off session support for your app, add the following to your .sailsrc file:

"hooks": {
  "session": false
}

This disables the core Sails session hook. You can also accomplish this by setting the sails_hooks__session environment variable to false.

Disabling sessions for certain requests
#

To turn off session support on a per-route (or per-request) basis, use the sails.config.session.isSessionDisabled setting. By default, Sails enables session support for all requests except those that look like they're pointed at static assets like images, stylesheets, etc.

Is something missing?

If you notice something we've missed or could be improved on, please follow this link and submit a pull request to the sails repo. Once we merge it, the changes will be reflected on the website the next time it is deployed.

Concepts