Node and Express - Sessions
Learn what sessions are, the express-session library, how sessions work with cookies to add persistence to an application, the difference between a session and a cookie, session security, what a session cookie is, and how to customize a session cookie.
Table of Contents 📖
- What is a Session?
- Why do we need Sessions?
- Cookies vs Sessions
- How do Sessions Work?
- Installing express-session
- What is express-session?
- express-session with cookie-parser?
- Registering expression-session as Global Middleware
- Passing Options to express-session
- What is the Session Secret?
- Session Secret Security
- Checking for our Session Cookie
- Customizing the Session Cookie
- Changing our /login Route Middleware
- Creating a Login Method
- What is findOne()?
- Adding User Object to Session
- Checking a User Already has a Session Set
- Deleting the Session at the /logout Route
- Summary
What is a Session?
A session is a series of browser requests that come from the same client during a given time period.
Why do we need Sessions?
Websites use the HTTP protocol. HTTP is stateless, meaning that after each request and response the client and server forget everything about each other. If we want the client and server to remember one another, we can use a session. Sessions contain data about the client, allowing the server to keep track of the user.
Cookies vs Sessions
Both cookies and sessions are used for storing data across different page loads, but cookies are stored on the client while sessions are stored on the server. As such, sensitive information should not be stored on a cookie as anyone can access it. Instead, sensitive information should be stored on the server by using sessions.
How do Sessions Work?
When a client has successfully logged in, the server will create a session and store it. The server then responds to the client with a cookie that contains the session's unique id. This cookie is then sent with every request to the server. The server then looks at the session id in the cookie and looks up the saved session data, usually from a database.
So once more, session data is stored on the server, but a unique id is stored on the client in a cookie. This id is then used by the server to get relevant information about the user. Therefore, sessions work with cookies in tandem.
Installing express-session
To use sessions with Express, we need to download the express-session library from npm.
npm install express-session
What is express-session?
The library express-session creates a session middleware to handle sessions in Express. express-session handles everything for us including creating the session, setting the session cookie, and creating the session object on the request object.
express-session with cookie-parser?
As sessions use a cookie to store an id, we need to create a cookie when we create a session. Before express-session version 1.5.0, the cookie-parser middleware would need to be used alongside express-session. However, in this article we have downloaded express-session version 1.17.2 which takes care of cookie parsing for us. So we don't need to make use of the cookie-parser middleware. So, remove the cookie-parser as global-middleware from our Express application. To do this, remove the code below from our main index.js file.
const cookieParser = require('cookie-parser');
app.use(cookieParser());
We can also uninstall it with npm by running the following.
npm uninstall cookie-parser
Registering expression-session as Global Middleware
Lets register express-session as global middleware. This will be done inside our main index.js file.
const session = require('express-session');
app.use(session());
Passing Options to express-session
The session function that we passed to app.use() takes an object as an argument. One option to pass is secret.
app.use(session({
secret: 'thisismysecretdonttellanyone!'
}));
What is the Session Secret?
The session secret is used to create a hash to sign the session id cookie. This prevents the cookie from being tampered with. In other words, signing the session id cookie allows us to tell if the client modified the cookie. The session cookie set on the client will have the following format.
sid.signature
Sid is the session ID and signature is a signature generated by signing the sid with the secret provided to the express-session middleware. When the cookie is read, the signature is recalculated and compared to the signature attached to the cookie.
Session Secret Security
The secret provided to the session should be a random string of characters that is changed periodically. However, for demonstration purposes this will suffice.
Checking for our Session Cookie
Now that our session middleware is set up, we can see the session cookie if we start up our application. Run the Express application and check the application tab in the browser.
Customizing the Session Cookie
We can customize the session cookie that is set on our browser. To do this, add a cookie property to the object provided to the session function.
app.use(session({
secret: 'thisismysecretdonttellanyone!',
cookie: {}
}));
Lets say we want to make it so the cookie is only sent along with requests in a first-party context. We can do this by setting SameSite to "strict".
app.use(session({
secret: 'thisismysecretdonttellanyone!',
cookie: { sameSite: 'strict' }
}));
Changing our /login Route Middleware
The pug form that we made a few articles back has been adding users to our database. Lets change it to login users that are already registered instead. To do this, lets change the controller function that is called when a POST request is made to /login. Replace user.create with user.login inside routes/login/index.js.
router.post('/', user.login);
Creating a Login Method
Now, lets create a login method inside our user controller. Add the following to module.exports in controllers/user.js.
login: async (request, response) => {
}
What we want to do is use Sequelize to search for the username and password that were entered into the form. If those two match, then we send the user to their profile, if not we render the login form again.
if (request.body.username && request.body.password) {
const { username, password } = request.body;
let user = await User.findOne({
where: { username, password }
});
if (user) {
response.render('profile', { username });
}
else {
response.render('login');
}
}
What is findOne()?
The method findOne() is a Sequelize model method that searches for one row in the provided model using the provided arguments. Here, we are searching for a row in our user model where the username and password match the ones provided in the form. If the findOne method finds a row with the provided information it returns it. If not, it will return null.
Adding User Object to Session
Remember, the express-session middleware creates a session for us. We can access this session object in our middleware using the request.session object. Lets add our user object to the session object if the login was successful.
request.session.user = user;
Lets also tag on another property to our session object that tells our application the user has been authorized.
request.session.authorized = true;
Checking a User Already has a Session Set
If the user already has a session set with the authorized property, then lets lets skip the login page and send them right to their profile. Add the following to our /login GET route in routes/login/index.js
if (request.session.authorized) {
response.render('profile', { username: request.session.user.username });
} else {
response.render('login');
}
Now, if we visit the main page with an authenticated session, we will be directed to our profile page.
Deleting the Session at the /logout Route
By default, our session cookie will expire when the browser (not the tab) is closed. This is because the max-age/expires column is set to session, meaning expire when the browser closes. We can easily change this by setting the maxAge property of the cookie. However, there are times when we want to manually delete the session cookie, such as when the client logs out. To delete the session cookie we use the method request.session.destroy(). So, add the following to our routes/logout/index.js file.
router.get('/', (request, response) => {
request.session.destroy();
response.redirect('/login');
});
Now, when the user logs out of the application, the session cookie will be destroyed and they will be redirected to the login page.
Summary
But there we have it! If this article was helpful, please consider donating by using the link at the top of the page, sharing this article, and subscribing to my YouTube channel WittCode!