Advanced

Authentication and passwords

Never store passwords in plain text. Learn to hash passwords with bcrypt so a stolen database is useless, and use sessions to keep a user logged in.

What you will see

Session created for sess_123456, authenticated: True In-memory store works for a simple app: True

About 2 min of reading

Try It

Remember logged-in users with sessions or tokens

1

Issue a session id (cookie) and a token.

def make_session_id(): return "sess_123456" def make_token(user): return "tok_%s" % user print() print("Authentication and passwords") print("Session id (cookie): %s" % make_session_id()) print("Token (browser sends with every request): %s" % make_token("alice"))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Authentication and passwords Session id (cookie): sess_123456 Token (browser sends with every request): tok_alice
2

Store sessions server-side and verify them.

session_store = {} def login(user): sid = make_session_id() session_store[sid] = user return sid def authenticated(sid): return sid in session_store sid = login("bob") print("Session created for %s, authenticated: %s" % (sid, authenticated(sid))) print("In-memory store works for a simple app: True")

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Session created for sess_123456, authenticated: True In-memory store works for a simple app: True

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

The password is hashed before storage, and at login the entered password is hashed again and compared to the stored hash.
1 User submits a password
↓
2 The app hashes it with the same salt
↓
3 compareSync reads the stored hash
↓
4 The two hashes are compared
↓
5 Match means the session is created
↓
6 A session cookie is issued to the browser
Why this works
When you have a choice

Sessions vs tokens

Two main ways to remember a logged-in user:

  • **Sessions*: the server keeps a record on its side and sends the browser a cookie with a random session id. Simple and secure.
  • **Tokens*: the server issues a signed token the browser keeps and sends with every request. Better for APIs and mobile apps.

Express sessions need a cookie plus a store. For a simple app the in-memory store is fine; for many servers you swap in a Redis store so any server can validate the session.

Examples

Hashing a password

A hash turns a password into a fixed string that cannot be reversed. Store the hash, never the password.

const bcrypt = require('bcrypt');

async function signup(email, password) {
  const salt = bcrypt.genSaltSync(10);
  const hash = bcrypt.hashSync(password, salt);
  // store `hash` in the database
  return hash;
}

The same password always produces the same hash, but the hash looks nothing like the password. If attackers leak your database, they only get hashes, worthless on their own.

Checking a login

On login, hash the entered password the same way and compare:

async function login(email, password) {
  const row = await getUserByEmail(email);
  if (!row) return false;
  return bcrypt.compareSync(password, row.password_hash);
}

compareSync returns true only when the password matches. This is why a database leak of hashes is safe, no one can turn them back into passwords, and they only match the exact original.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Middleware and request flow
Previous Lesson
Middleware and request flow
Next Lesson
Error handling and validation
Error handling and validation →