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.
const hash = await bcrypt.hash(password, 12); // on signup
const ok = await bcrypt.compare(password, hash); // on loginMD5 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/981to/applications/982must not show someone else's data.
OWASP essentials
| Risk | Defence |
|---|---|
| SQL injection | parameterised queries / ORM, never string concatenation |
| XSS | escape output, Content-Security-Policy, HttpOnly cookies |
| CSRF | SameSite cookies, CSRF tokens for cookie-auth forms |
| Broken access control | check ownership on every request |
| Brute force | rate limiting, lockouts, CAPTCHA after failures |
| Secrets leak | env vars or a secret manager, rotate keys |
| Outdated packages | npm audit, Dependabot, update regularly |
// 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.