Skip to main content

Using Session Cookies For Login

One way to handle login functionality into your web app is by using cookies.

What are Cookies?

A cookie is an HTTP header that is stored and managed by the browser, linked to your domain with a handful of security rules enforced by your browser.

At a high level, when you make a request to a website, that website can set a cookie in your browser. This happens all the time on the web, and its a common way to "track" users because cookies can stick around.

Let's say you have a website, hosted at www.example.com

Check out the MDN docs for more in-depth details on Cookies

Implementing Cookies

Let's setup a server route /login, that expects a JSON body of { "username": "my-user" }, and replies with a cookie containing that username.

app.get("/login", (req, res) => {
const username = req.body.username
res.cookie('logged-in', 'username')
res.send()
});

You can work with API requests & Cookies using Postman to get started. The examples below use fetch(), but use the example to fill in the request on Postman.

fetch('http://localhost:8000/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({username: 'danny'})
})
  • You should see in the response Set-Cookie: logged-in=danny; Path/;
  • At this point, Postman will be managing your Cookie header.

Let's add a /dashboard route that returns some html that displays your logged-in cookie value.

app.js
app.get("/dashboard", (req, res) => {
const cookie = req.headers['cookie'].split(';').find(cookie => {
const [key, value] = cookie.split('=')
return key === 'logged-in'
})
res.send(`<html><body>${cookie}</body></html>`)
});

Then you made a request to /dashboard

fetch('http://localhost:8000/dashboard')

You would see in the response

<html>
<body>logged-in=danny</body>
</html>

That's because Postman included the cookie in your next request to localhost:8000. The browser behaves the same way.

At this point, this cookie will be attached to every request to your site until its explicitly cleared. That's pretty convenient.

This pattern is used for:

  • tracking - once you visit a website, the website can know when you've revisited. This is used in many popular analytics tools, like Google Analytics.
  • user preferences - if you enjoy dark mode in your browser, you can save the user's preferred light/dark mode in a cookie
  • cookie consent - one way of recording that a user has accepted a cookie consent form is by setting a cookie.

Downside

Since the content of the cookie is in plain-text, anyone can send a request with a cookie if you know what the cookie header is supposed to contain, like the example below.

fetch('http://localhost:8000/dashboard', {
headers: {
'Cookie': 'logged-in=danny'
}
})

*Your browser won't let you send a fetch() request with a Cookie header, since it is reserved for use by your browser. So this only works in Postman or in Node.

cookie-session is an NPM package to make it easier to work with cookies for user sessions in an Express application:

  • It creates a cookie named session and it encrypts/decrypts any data that you add to it.
  • You can then read/write that cookie from any Express Route
caution

This pattern described ONLY works if your frontend and your backend are available under the same domain. Otherwise, they can't share cookies.

If you have a frontend running on localhost:8000 and a backend running on localhost:5000, your backend would not be able to set a cookie because the request is coming from a different domain. This is enforced by the browser.

What is a Session?

It's helpful to define the word "session". A session is the period of time that we want to track - most commonly for logged-in user sessions. Here's an example for Milestone 2.

This is already included as part of milestone-2, but here is what the setup looks like:

From the backend/ folder, run npm install --save cookie-session.

  • cookie-session manages the user's session as a cookie
  • cookie-parser makes it easy to work with cookies in express

Update backend/app.js to use cookie-session

backend/app.js
import cookieSession from "cookie-session";
...

const app = express();
app.use(
cookieSession({
secret: "cookiesecret",
})
);

Start a session by adding something to req.session. Let's use /api/login as an example:

app.post("/api/login", (req, res) => {
...
req.session.username = req.body.username;
return res.status(200).send();
});

This route expects a body containing a username, and will then set that username in the user's session so we can identify them later on. req.session.username is all it takes to save a JSON object in a cookie.

If you make this request with Postman, you'll see this in the response:

screenshot showing new session cookie in Postman

info

In a real application, you would check the username & password, before adding some properties to the session to represent a logged in user. Since milestone-2 is a prototype, we're ignoring that part for now.

We'll get to use real authentication for milestone 3.

Read from that session in any other route. For example:

app.get('/api/classes', (req, res) => {
if (req.session.username) {
console.log('username is present!')
} else {
console.log('user is not present!')
}
...
})
caution

You need to check for a property in the session specifically, you can't do if (req.session) { ... }

req.session is a real object created by cookie-session that will be present in all requests.

However, the properties within req.session might not, i.e. req.session.username for example.

Destroy the user's session: Check the cookie-session docs to find out how to destroy a session.