WEEK 13 · JOURNEY'S END

Sessions, Auth & Going Live

The final island. HTTP is forgetful β€” so how does an app know who you are between clicks? This week: authentication, sessions & cookies, password hashing, JSON Web Tokens, and finally β€” deploying your app so the whole world can visit. πŸ”πŸš€

This week stateless HTTP authentication cookies & sessions bcrypt JWT deploy πŸš€

0 Week at a glance

The last leg: teach your app to remember who you are, keep it secure, and put it online.
  • Stateless HTTP β€” the server forgets you the instant a request ends. Sessions & tokens fix that.
  • Authentication β€” proving who you are. Three flavours: Basic, Session, Token.
  • Sessions & cookies β€” the server keeps your data; the browser carries a tiny session id cookie.
  • Hashing β€” never store raw passwords; store a one-way bcrypt hash.
  • JWT β€” a signed header.payload.signature token the client carries instead of a session.
  • Deploy β€” push to GitHub, host on Render, get a live URL. πŸŽ‰

Everything here is playable. Toggle the stateless server, run the session handshake, log in and out in the live session lab, forge and break a JWT, and deploy to Render β€” all in your browser, no setup.

New guided project πŸ”‘ β€” the Week 11 Anime Collection app, now with a real login & logout (express-session + bcrypt). Browsing is public; adding & deleting need a login. Play it live, then build the auth flow file by file β†’ Anime Collection + Login.

1 HTTP forgets everything 🐟

Key idea stateless request β†’ response no memory
Every request runs the app from scratch. It doesn't remember the last request β€” or even who sent it.
  • An interaction is a sequence of requests and responses.
  • For each request, the app starts over β€” no data is kept between consecutive requests.
  • It can't tell whether two requests came from the same user or different users.
  • So we must transfer data from one request to the next β€” that's what sessions and tokens do.

Flip the switch and send a few requests. Without a session the server is a goldfish; with one, it remembers you.

What the server remembers about you
β€”

2 Authentication β€” who are you?

Authentication is the process of verifying a user's identity β€” making sure users are who they claim to be. (Different from authorization, which is what they're allowed to do.)

Three ways to prove identity across stateless requests:
MethodHow it worksVerdict
BasicSend username + password with every requestvery insecure
SessionLog in once β†’ server stores a session, browser holds a session-id cookiecommon in server-rendered apps
TokenLog in once β†’ get a signed token, send it on each requestgreat for APIs & SPAs

3 Basic authentication

Basic auth creds every request no login/logout insecure
  • The browser sends the username & password with every single request.
  • There's no login or logout system β€” nothing to start or end.
  • It's very insecure: credentials fly across the wire constantly.
  • Not used in modern apps because of the security risk.

Basic auth is the "shout your password at the guard every time you walk past" approach. We show it so you recognise it β€” then we use sessions or tokens instead.

4 Session authentication

The handshake log in once server makes a session id stored in a cookie sent every request
  1. The user logs in with a username & password.
  2. If login succeeds, the server creates a session id linked to that user.
  3. The session id is sent back to the browser and stored in a cookie.
  4. On the next request, the browser automatically sends the session id β€” so the server knows it's you.

Press play and watch the session id travel:

1browser POST /login with username + password
2server verify creds β†’ create session { user: "Light" }
3server Set-Cookie: connect.sid=abc123
4browser stores the cookie πŸͺ
5browser GET /profile  (cookie: connect.sid=abc123)
πŸͺ Press play to watch a login turn into a session cookie.

5 Cookies vs sessions

A cookie lives in the browser; a session lives on the server. The cookie usually just carries the session's id.
πŸͺ CookiesπŸ—„οΈ Sessions
Stored client-side (in the browser)Store user data server-side
Limited in size by the browser (~4 KB)Limited only by server space
The user can read & change themCannot be modified by users
Can be disabled by the userExpire when the browser is closed
You send data itselfIdentified by a session id β€” often a small cookie

What are sessions good for? Authentication (is this user logged in?), shopping carts (hold items as the user browses), and user preferences (theme, language).

6 The session identifier

The session id… made by the server held by the browser sent every request forgotten on close
  • Is generated by the server when a session starts.
  • Is remembered by the browser (kept in a cookie).
  • Is sent with every further HTTP request to that server.
  • Is forgotten when the session ends or the browser is closed.

Session variables store information only temporarily. For data that must survive β€” accounts, orders, anime collections β€” you still need a database (hello, Week 12 πŸ—„οΈ).

7 Sessions in Express.js

Steps install app.use(session(...)) req.session.x = … req.session.destroy()
install
npm install express-session bcryptjs

Set up the session middleware

app.js
const express = require("express");
const session = require("express-session");
const app = express();

app.use(session({
  secret: "your-secret-key",   // signs the session-id cookie
  resave: false,               // don't re-save if nothing changed
  saveUninitialized: true,     // save new, empty sessions too
  cookie: { secure: false }    // true only over HTTPS
}));
  • secret β€” a random string used to sign the session-id cookie so it can't be forged.
  • resave β€” whether to re-save the session on every request even if it didn't change.
  • saveUninitialized β€” whether to store new but empty sessions.
  • cookie β€” configures the cookie (expiry, secure, etc.).

Use the session in your routes

req.session is just an object that survives between requests. Write to it in one route, read it in another.
routes
app.get("/login", (req, res) => {
  req.session.username = "Light";        // store data in the session
  res.send("Logged in!");
});

app.get("/profile", (req, res) => {
  const user = req.session.username;      // read it back next request
  res.send(`Welcome, ${user || "Guest"}`);
});

app.get("/logout", (req, res) => {
  req.session.destroy();                  // end the session
  res.send("Logged out.");
});

8 Live session lab πŸ§ͺ

Click the routes and watch the πŸͺ browser cookie and the πŸ—„οΈ server session store react in real time.
πŸͺ Browser cookie
πŸ—„οΈ Server session store
{ }

Try /profile first β€” you're a Guest because there's no cookie. Then /login (a session appears on both sides), /profile again (recognised!), and /logout β€” the store empties and the cookie is cleared.

9 Never store raw passwords β€” hash them

Hashing one-way deterministic bcrypt + salt

A cryptographic hash function turns any input into a fixed-length string that is one-way β€” you can't reverse it back to the original. Store the hash of a password, never the password itself.

Type a password and watch its hash. Change one letter β€” the whole hash changes (the avalanche effect):

hash
bcrypt adds a random salt, so two identical passwords hash differently β€” and it's deliberately slow to resist guessing.

That's why we install bcryptjs. You hash on registration and compare on login β€” never decrypt:

hash.js
const bcrypt = require("bcryptjs");

// REGISTER β€” hash the password before saving it
const hash = bcrypt.hashSync("anime123", 10);   // 10 = salt rounds
console.log("stored hash:", hash);

// note: hashing the SAME password again gives a DIFFERENT hash (salt!)
console.log("again:      ", bcrypt.hashSync("anime123", 10));

// LOGIN β€” compare the typed password against the stored hash
console.log("correct pw β†’", bcrypt.compareSync("anime123", hash));
console.log("wrong   pw β†’", bcrypt.compareSync("password", hash));

Teaching emulator. This page uses a simplified hash so it runs in your browser. Real bcryptjs uses the Blowfish-based bcrypt algorithm β€” same idea (salted, one-way, slow), stronger maths.

10 Token authentication

A token is… encrypted compact short-lived

A token is a digital credential that proves your identity to a system β€” encrypted, compact, and short-lived. Think of it as a festival wristband: flash it, don't re-buy your ticket.

  1. Login β€” the user signs in with credentials once.
  2. Receive token β€” on success, the server issues an auth token.
  3. Authenticate β€” the token is sent on subsequent requests; no password re-entry.
  4. Expiration β€” tokens are time-sensitive and expire after a set period.

Session vs token

Session authToken auth
State kept onthe server (a session store)nowhere β€” the token itself carries it (stateless)
Client storesa session-id cookiethe whole token (header/localStorage)
Scales to many serversneeds shared session storageeasy β€” any server can verify the signature
Great forserver-rendered sitesAPIs, mobile apps, SPAs

Why tokens are considered secure: they're encrypted/signed, they have a limited lifespan, and the server does not store your password to check them.

11 JSON Web Tokens (JWT)

JWT structure header payload (claims) signature

A JSON Web Token (pronounced "jot") is a string the server produces that encodes JSON data. It has three dot-separated parts: header.payload.signature. The payload holds claims (like your username); the signature proves it wasn't tampered with.

Forge a token, decode its parts, then tamper with the payload and hit Verify β€” the signature check catches it:

header
payload
signature

The jsonwebtoken module

install
npm install jsonwebtoken

Run it β€” sign a token, then verify it. Then change the secret in verify and watch it reject the token:

jwt.js
const jwt = require("jsonwebtoken");

const secret  = "supersecret";
const payload = { username: "Light" };

// sign β†’ the string handed to the client after login
const token = jwt.sign(payload, secret, { algorithm: "HS256" });
console.log("Token:", token);

// verify β†’ checks the signature, returns the decoded claims
const decoded = jwt.verify(token, secret, { algorithms: ["HS256"] });
console.log("Decoded username:", decoded.username);

// tamper check β€” a wrong secret fails verification
try {
  jwt.verify(token, "WRONG-secret");
} catch (e) {
  console.log("Rejected:", e.message);
}

Signed, not secret. A JWT's header & payload are only base64-encoded β€” anyone can read them. The signature stops tampering, not reading. Never put passwords or secrets in a JWT payload.

12 Deploying with GitHub

Your code goes online on GitHub first β€” that's what deployment platforms connect to.
  • Store your project online and track changes with version control.
  • Connect easily to deployment platforms (like Render).

Before you push, tick the pre-flight checklist (click each item):

  • Add a .gitignore that excludes node_modulesDependencies are re-installed by the host β€” don't commit them.
  • Never commit secrets β€” keep them in .envConnection strings, JWT secrets and API keys stay out of git.
  • Read the port dynamically: process.env.PORT || 3000The host assigns a port; hard-coding 3000 breaks deploys.
  • Use a cloud database β€” MongoDB Atlas is onlineA local DB isn't reachable from Render.
  • Make sure the project runs locally before pushingIf it doesn't start on your machine, it won't start on theirs.

13 Deploying with Render πŸš€

Render steps New β†’ Web Service connect repo npm install node app.js
  1. Go to render.com and create an account.
  2. Click New β†’ Web Service.
  3. Connect your GitHub repository.
  4. Configure the commands: build npm install, start node app.js.
  5. Make sure your MongoDB is online (Atlas) and the port is dynamic.
app.js β€” the one line that matters for deploy
const PORT = process.env.PORT || 3000;   // Render sets PORT for you
app.listen(PORT, () => console.log("Live on port " + PORT));

Hit deploy and watch Render build your app β€” it ends with a live URL anyone can visit:

render.com Β· deploy log

After deployment Render gives you a live URL like https://anime-collection.onrender.com β€” your full-stack app, running for the world. That's the finish line. 🏁

14 Key takeaways

  1. HTTP is stateless β€” each request starts fresh, so we transfer identity with sessions or tokens.
  2. Authentication verifies who you are. Basic (creds every request) is insecure; use Session or Token.
  3. Session auth: log in once β†’ the server stores a session and the browser holds a session-id cookie it sends every request.
  4. Cookies live in the browser (small, user-editable, can be disabled); sessions live on the server (bigger, safe, expire on close).
  5. In Express: app.use(session(...)), then read/write req.session; end it with req.session.destroy().
  6. Never store raw passwords β€” hash them with bcrypt (salted, one-way): hashSync to register, compareSync to log in.
  7. A JWT is a signed header.payload.signature. jwt.sign() issues it; jwt.verify() checks the signature β€” tampering is rejected.
  8. Deploy: push clean code to GitHub (.gitignore, no secrets, dynamic PORT, cloud DB), connect it on Render, get a live URL. πŸš€

15 Self-check quiz

No grades, instant feedback. Ten questions across the whole finale β€” sessions, hashing, JWT and deployment.

Q1. Why does a web app need sessions or tokens at all?

Each request runs the app from scratch with no memory of the last one β€” it can't even tell if two requests came from the same user. Sessions/tokens carry identity across that gap.

Q2. Which authentication method sends the username & password on every request and is considered very insecure?

Basic auth attaches credentials to every request and has no login/logout β€” modern apps avoid it in favour of sessions or tokens.

Q3. In session authentication, where is the session data kept, and what does the browser hold?

The server stores the session; the browser only carries the session id in a cookie and sends it back with every request.

Q4. Which statement about cookies vs sessions is correct?

Cookies are client-side (small, user-editable, can be turned off). Sessions are server-side (limited by server space, safe from user tampering, expire when the browser closes).

Q5. Which line ends a user's session in Express?

req.session.destroy() clears the server-side session β€” the standard way to log a user out.

Q6. Why do we store a hash of a password instead of the password itself?

A cryptographic hash is one-way β€” you can't reverse it. On login you compare the typed password's hash to the stored one; you never decrypt anything.

Q7. What is the correct order of a JWT's three parts?

A JWT is header.payload.signature β€” three base64url parts joined by dots. The payload carries the claims; the signature proves integrity.

Q8. A JWT arrives with its payload edited but the original signature. jwt.verify() will…

The signature is computed over header.payload with the secret. Change the payload and the recomputed signature won't match β€” verify throws. That's the whole point.

Q9. Which of these should you set so your app works after deploying to Render?

The host assigns a port at runtime via process.env.PORT. Read it dynamically (falling back to 3000 locally) or the deploy won't bind correctly.

Q10. Before pushing to GitHub for deployment, which is a must-do?

Exclude node_modules with .gitignore (the host reinstalls it) and never commit secrets β€” connection strings and JWT secrets live in .env.

16 Reading & cheat sheet

Brightspace β€” Week 13. A complete step-by-step token-based authentication & full-stack deployment tutorial, plus all the files zipped, are posted under Week 13.

Grab the Auth & Deploy cheat sheet

πŸ” Sessions, JWT & Deploy
Week 13 one-page cheat sheet: stateless HTTP, session auth flow, express-session setup, bcrypt hashing, JWT sign and verify, cookies vs sessions, deploy checklist and Render steps

Guided capstone project

Anime Collection + Login. The Week 11 MVC app, upgraded with a simple session login/logout (express-session + bcrypt): browsing is public, but add and delete require logging in. Play the finished app right in your browser, then build the auth flow file by file.

Stretch: move the users into MongoDB (Week 12) with hashed passwords, add a register page, then deploy it to Render.