← Backend development with Node.js and REST APIs

Lesson 3 of 5

Authentication, authorisation and security

3 min read

Authentication vs authorisation

  • Authentication: who are you? (login)
  • Authorisation: what may you do? (a recruiter can see applicants only for their own company's jobs)

Storing passwords

Never store plain or reversibly encrypted passwords. Hash with a slow, salted algorithm: bcrypt, scrypt or argon2.

js
const hash = await bcrypt.hash(password, 12);   // on signup
const ok = await bcrypt.compare(password, hash); // on login

MD5 or SHA-256 alone is too fast: attackers can test billions of guesses per second.

Sessions vs JWT

Server sessions: the server stores a session id → user mapping (in Redis or a DB) and sends the id in an HttpOnly cookie. Easy to revoke (delete the session), small cookie.

JWT (JSON Web Token): a signed token containing claims ({ sub: 42, role: "student", exp: … }). The server verifies the signature without a lookup, which suits stateless services. Downsides: hard to revoke before expiry (use short lifetimes plus refresh tokens, or a version number checked against the DB), and the payload is only encoded, not encrypted: never put secrets in it.

Where to keep tokens in a browser: an HttpOnly, Secure, SameSite cookie (JavaScript can't read it, which protects against XSS theft) rather than localStorage.

OAuth 2.0 / "Login with Google"

Your app redirects the user to Google, the user consents, Google redirects back with a short-lived code, and your server exchanges the code for tokens. Use the authorisation code flow with PKCE. You never see the user's Google password.

Authorisation patterns

  • Role-based access control (RBAC): roles like admin, recruiter, viewer with permissions.
  • Always check ownership on the server: WHERE id = $1 AND company_id = $2. Hiding a button in the UI is not security.
  • Insecure direct object reference (IDOR): changing /applications/981 to /applications/982 must not show someone else's data.

OWASP essentials

RiskDefence
SQL injectionparameterised queries / ORM, never string concatenation
XSSescape output, Content-Security-Policy, HttpOnly cookies
CSRFSameSite cookies, CSRF tokens for cookie-auth forms
Broken access controlcheck ownership on every request
Brute forcerate limiting, lockouts, CAPTCHA after failures
Secrets leakenv vars or a secret manager, rotate keys
Outdated packagesnpm audit, Dependabot, update regularly
js
// Vulnerable
db.query(`SELECT * FROM users WHERE email = '${email}'`);
// Safe
db.query("SELECT * FROM users WHERE email = $1", [email]);

Also: HTTPS everywhere, security headers (helmet), CORS restricted to your own front-end origins, and file uploads checked for type and size and stored outside the web root.

Check yourself

  • Why bcrypt instead of SHA-256 for passwords?
  • How would you log out a user who has a JWT?
  • What is IDOR and how do you prevent it?

Sign up free to save your progress.