Session Cookies in Node.js
HTTP protocol: It is the backbone of the internet every single request from the client for particular contains several HTTP headers and that contains all the information of the request. This protocol is the foundation of the data exchange over the internet but the HTTP protocol is the stateless protocol means this protocol cannot be able to maintain the past requests of the particular client to the server. It means we have to give again and again authorized requests in order to move forward to the next page of the particular page of a web application then how to overcome this problem. The answer is cookies and sessions. Cookies and sessions make the HTTP protocol stateful protocol.
Session cookies: Session cookies are the temporary cookies that mainly generated on the server-side.The main use of these cookies to track all the request information that has been made by the client overall particular session. The session is stored for a temporary time when the user closes the browser session automatically destroys it. In this article, we will be using external file storage in order to store session cookies. Example of session cookies the most common example of session cookies are an e-commerce website. All e-commerce website initializes a session when a new user starts the particular e-commerce website. When a session is created after successful authorization a unique session id is created on the client-side in the form of a cookie. So that after the first request this generated cookie on the client-side will help for authentication of the user with the session on the client-side and session track all the new request’s information and response the past tracked information to the client.
Installing Modules:
npm install express-session
- session-file-store: This module helps to create a new file-store for the new session.
Project Structure: Our project structure will look like this:
Filename: index.js
Session Cookie Authentication in Node.js (With Complete Examples)
In this post, we will learn how to authenticate users using session cookies in a Node.js server application.
When a user logs into our application, we need to know who they are across all our HTTP methods and routes.
One way to do this is to store the users “session”. A session is started once a user logs in, and expires some time after that.
Each logged in user has some reference to the session (called a cookie), which they send with their requests. We then use this reference to look up the user that it belongs to and return information specific to them.
If you just want to see the source code for this tutorial, you can find it here
Overview#
In this post, we will look at how to create and store the session of a logged in user as a cookie on the browser.
- The /signin route will accept a users username and password, and set a session cookie if successful.
- The /welcome route will be a simple HTTP GET route which will show a personalized message to the currently logged in user.
The session information of the user will be stored in our applications local memory.
We will also assume that the users that are signing in have already created their username-password credentials on our application.
Creating the HTTP Server#
Before we begin, we need to install the libraries that we will use for this example:
npm install express body-parser cookie-parser uuid
Let’s create an index.js file and initialize the HTTP server with the required routes:
const express = require('express') const bodyParser = require('body-parser') const cookieParser = require('cookie-parser') const < signinHandler, welcomeHandler, refreshHandler > = require('./handlers') const app = express() app.use(bodyParser.json()) app.use(cookieParser()) app.post('/signin', signinHandler) app.get('/welcome', welcomeHandler) app.listen(8080)
We can now define the /signin and /welcome routes.
Creating Session Tokens#
We will be creating a new session token every time a user signs in.
The /signin route will take the users credentials and log them in.
In order to make this simple, we’re storing the users information as an in-memory object in our code:
const users = "user1": "password1", "user2": "password2" >
For now, there are only two valid users in our application: user1 , and user2 .
We also need to define an object to store information about each users session:
// each session contains the username of the user and the time at which it expires class Session constructor(username, expiresAt) this.username = username this.expiresAt = expiresAt > // we'll use this method later to determine if the session has expired isExpired() this.expiresAt (new Date()) > > // this object stores the users sessions. For larger scale applications, you can use a database or cache for this purpose const sessions = <>
Next, we can implement the Signin HTTP handler:
const signinHandler = (req, res) => // get users credentials from the JSON body const < username, password > = req.body if (!username) // If the username isn't present, return an HTTP unauthorized code res.status(401).end() return > // validate the password against our data // if invalid, send an unauthorized code const expectedPassword = users[username] if (!expectedPassword || expectedPassword !== password) res.status(401).end() return > // generate a random UUID as the session token const sessionToken = uuid.v4() // set the expiry time as 120s after the current time const now = new Date() const expiresAt = new Date(+now + 120 * 1000) // create a session containing information about the user and expiry time const session = new Session(username, expiresAt) // add the session information to the sessions map sessions[sessionToken] = session // In the response, set a cookie on the client with the name "session_cookie" // and the value as the UUID we generated. We also set the expiry time res.cookie("session_token", sessionToken, < expires: expiresAt >) res.end() >
If a user logs in successfully, this handler will set a cookie on the client side, and inside its own local memory. Once a cookie is set on a client, it is sent along with every subsequent request.
We can use UUIDs to represent session tokens, since they are uniform in structure, and difficult to guess.
Authenticating Users Through Session Cookies#
Now that we have persisted the users session information on their client (in the form of the session_token cookie) and the server, we can write our /welcome handler to handle user specific information.
- Authenticate subsequent user requests
- Get information about the user making the request
Let’s write our /welcome handler to do that:
const welcomeHandler = (req, res) => // if this request doesn't have any cookies, that means it isn't // authenticated. Return an error code. if (!req.cookies) res.status(401).end() return > // We can obtain the session token from the requests cookies, which come with every request const sessionToken = req.cookies['session_token'] if (!sessionToken) // If the cookie is not set, return an unauthorized status res.status(401).end() return > // We then get the session of the user from our session map // that we set in the signinHandler userSession = sessions[sessionToken] if (!userSession) // If the session token is not present in session map, return an unauthorized error res.status(401).end() return > // if the session has expired, return an unauthorized error, and delete the // session from our map if (userSession.isExpired()) delete sessions[sessionToken] res.status(401).end() return > // If all checks have passed, we can consider the user authenticated and // send a welcome message res.send(`Welcome $userSession.username>!`).end() >
- If there is no session_token cookie along with the request (which means that the requestor hasn’t logged in)
- If the session token is not present in memory (which means that the requestor is sending us an invalid session token)
- If the session has expired
- Since the session tokens are randomly generated, its near-impossible for a malicious user to brute-force their way into a users session.
- If a users session token is compromised somehow, it cannot be used after its expiry. This is why the expiry time is restricted to small intervals (a few seconds to a couple of minutes)
Refreshing Session Tokens#
Since the expiry time of a session token is kept small, we need to issue a new token often to keep the user logged in.
Of course, we cannot expect the user to login every time their token expires. To solve this, we can create another route that accepts the users current session token, and issues a new session token with a renewed expiry time.
Lets define a handler to renew the users session token every time they hit the /refresh route in our application
const refreshHandler = (req, res) => // (BEGIN) The code from this point is the same as the first part of the welcomeHandler if (!req.cookies) res.status(401).end() return > const sessionToken = req.cookies['session_token'] if (!sessionToken) res.status(401).end() return > userSession = sessions[sessionToken] if (!userSession) res.status(401).end() return > if (userSession.isExpired()) delete sessions[sessionToken] res.status(401).end() return > // (END) The code until this point is the same as the first part of the welcomeHandler // create a new session token const newSessionToken = uuid.v4() // renew the expiry time const now = new Date() const expiresAt = new Date(+now + 120 * 1000) const session = new Session(userSession.username, expiresAt) // add the new session to our map, and delete the old session sessions[newSessionToken] = session delete sessions[sessionToken] // set the session token to the new value we generated, with a // renewed expiration time res.cookie("session_token", newSessionToken, < expires: expiresAt >) res.end() >
We can now add this to the rest of our routes:
app.post('/refresh', refreshHandler)
Logging Out Our Users#
If the user decides to logout of our application, we need to remove their session token from our storage as well as the users client.
Let’s create a handler to implement this:
const logoutHandler = (req, res) => if (!req.cookies) res.status(401).end() return > const sessionToken = req.cookies['session_token'] if (!sessionToken) res.status(401).end() return > delete sessions[sessionToken] res.cookie("session_token", "", < expires: new Date() >) res.end() >
We can now add this to the rest of our routes:
app.get('/logout', logoutHandler)
Running Our Application#
To start the application, we can run:
Now, using any HTTP client with support for cookies (like Postman, or your web browser) make a sign-in request with the appropriate credentials:
POST http://localhost:8080/signin
You can now try hitting the welcome route from the same client to get the welcome message:
GET http://localhost:8080/welcome
Hit the refresh route, and then inspect the clients cookies to see the new value of the session_token :
POST http://localhost:8080/refresh
Finally, call the logout route to clear session data:
GET http://localhost:8080/logout
Calling the welcome and refresh routes after this will result in a 401 error.
You can find the working source code for this example on Github.