Summary
Master authentication and authorization concepts for security interviews. This comprehensive guide covers core principles, implementation patterns, security protocols (OAuth, SAML, OIDC), and best practices for building secure authentication systems. Essential for roles involving identity management, API security, and access control.
Core Concepts
Authentication vs Authorization - The Foundation
| Aspect | Authentication (AuthN) | Authorization (AuthZ) |
|---|---|---|
| Question | "Who are you?" | "What can you do?" |
| Process | Identity verification | Permission granting |
| Methods | Passwords, biometrics, tokens | ACLs, RBAC, ABAC |
| Failure Code | 401 Unauthorized | 403 Forbidden |
| Order | ALWAYS first | ALWAYS second |
| Time Complexity | O(1) - lookup | O(n) - permission check |
Key Terms
- Principal: The entity being authenticated (user, service, device)
- Credentials: Proof of identity (password, token, certificate)
- Claims: Statements about the principal (name, role, permissions)
- Identity Provider (IdP): Service that authenticates users
Authentication Methods
1. Password-Based
// Basic password hashing (bcrypt)
const bcrypt = require('bcrypt');
const saltRounds = 10;
// Register
const hashedPassword = await bcrypt.hash(plainPassword, saltRounds);
// Login
const isValid = await bcrypt.compare(plainPassword, hashedPassword);
Best Practices:
- Always hash passwords (bcrypt, scrypt, Argon2)
- Enforce strong password policies
- Implement rate limiting
- Use secure password reset flows
2. Multi-Factor Authentication (MFA)
Authentication Factors (need 2+ for MFA):
- Knowledge (Something you know): Password, PIN, security questions
- Possession (Something you have): Phone, hardware token, smart card
- Inherence (Something you are): Fingerprint, face, retina
- Location (Somewhere you are): GPS, IP geolocation
- Behavior (Something you do): Typing pattern, gait
// TOTP Implementation (RFC 6238)
const speakeasy = require('speakeasy');
// Generate secret (one-time during setup)
const secret = speakeasy.generateSecret({
length: 32, // 160-bit entropy
name: 'MyApp:user@example.com',
issuer: 'MyApp'
});
// Generate QR code URL
const qrUrl = secret.otpauth_url;
// Verify token (every login)
const verified = speakeasy.totp.verify({
secret: secret.base32,
encoding: 'base32',
token: userToken,
window: 2 // Allow 1 period before/after (30s window)
});
// Security: TOTP resistant to replay attacks due to time window
3. Token-Based Authentication
JWT (JSON Web Tokens) - Stateless Authentication
const jwt = require('jsonwebtoken');
// 🔐 Create token with security best practices
const token = jwt.sign(
{
userId: 123,
role: 'admin',
fingerprint: hashFingerprint // Prevent token theft
},
process.env.JWT_SECRET, // Min 256-bit secret
{
expiresIn: '15m', // Short-lived
algorithm: 'HS256', // Specify algorithm
issuer: 'myapp.com',
audience: 'myapp.com'
}
);
// ✅ Secure verification
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'], // Prevent algorithm switching attack
issuer: 'myapp.com',
audience: 'myapp.com'
});
} catch(err) {
// Handle: TokenExpiredError, JsonWebTokenError, NotBeforeError
}
JWT Anatomy (header.payload.signature):
- Header:
{"alg":"HS256","typ":"JWT"}(Base64URL) - Payload: Claims - registered (iss,sub,aud,exp,nbf,iat,jti), public, private
- Signature:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Security Considerations:
- Store in httpOnly cookies, not localStorage (XSS protection)
- Implement refresh tokens for long sessions
- Add token binding/fingerprinting
- Blacklist on logout (defeats stateless purpose)
Session Tokens
// Express session example
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JS access
maxAge: 3600000 // 1 hour
}
}));
4. OAuth 2.0 (RFC 6749)
Grant Types & Use Cases:
| Grant Type | Use Case | Security | Flow Complexity |
|---|---|---|---|
| Authorization Code + PKCE | SPAs, Mobile, Web | 🔐🔐🔐🔐🔐 | Complex |
| Client Credentials | M2M, Backend services | 🔐🔐🔐🔐 | Simple |
| Device Code | Smart TVs, CLI tools | 🔐🔐🔐 | Medium |
| Deprecated | 🔐 | Simple | |
| Legacy only | 🔐🔐 | Simple |
// OAuth 2.0 Authorization Code Flow with PKCE (RFC 7636)
// Essential for public clients (SPAs, mobile)
// Step 1: Generate PKCE challenge
const codeVerifier = generateRandomString(128); // 43-128 chars
const codeChallenge = base64UrlEncode(sha256(codeVerifier));
// Step 2: Authorization request
const authUrl = `https://auth.server.com/authorize?
client_id=${CLIENT_ID}&
redirect_uri=${REDIRECT_URI}&
response_type=code&
scope=openid profile email&
state=${generateNonce()}& // CSRF protection
code_challenge=${codeChallenge}&
code_challenge_method=S256`;
// Step 3: Token exchange (backend)
const tokenResponse = await fetch('https://auth.server.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authCode,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET, // Omit for public clients
redirect_uri: REDIRECT_URI,
code_verifier: codeVerifier // PKCE verification
})
});
// Response: access_token, refresh_token, id_token (if OIDC)
5. SAML (Security Assertion Markup Language)
- XML-based protocol for enterprise SSO
- Identity Provider (IdP) → Service Provider (SP)
- Uses digital signatures and encryption
6. OpenID Connect (OIDC)
- Identity layer on top of OAuth 2.0
- Provides ID tokens with user information
- Standardized user info endpoint
Security Best Practices
Token Security Best Practices
| Storage Location | Security | XSS Safe | CSRF Safe | Best For |
|---|---|---|---|---|
| httpOnly Cookie | 🔐🔐🔐🔐 | ✅ | Needs CSRF token | Session tokens |
| Memory (variable) | 🔐🔐🔐 | ✅ | ✅ | Access tokens |
| sessionStorage | 🔐🔐 | ❌ | ✅ | Temporary data |
| localStorage | 🔐 | ❌ | ✅ | Never for tokens! |
Implementation:
// Secure cookie configuration
app.use(session({
secret: crypto.randomBytes(64).toString('hex'),
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JS access
sameSite: 'strict', // CSRF protection
maxAge: 15 * 60 * 1000, // 15 minutes
domain: '.example.com', // Scope
path: '/' // Limit to path
}
}));
- Transmission:
- Always use HTTPS
- Include tokens in Authorization header
- Implement CSRF protection
Password Security Implementation
// Modern password validation (NIST SP 800-63B compliant)
class PasswordValidator {
constructor() {
// Load common passwords list (e.g., top 10,000)
this.commonPasswords = new Set(/* loaded from file */);
this.minEntropy = 20; // bits
}
validate(password, username = '') {
const errors = [];
// NIST: Minimum 8 chars, recommend 12+
if (password.length < 12) {
errors.push('Password must be at least 12 characters');
}
// NIST: Check against breach databases
if (this.commonPasswords.has(password.toLowerCase())) {
errors.push('Password found in breach database');
}
// Check for username in password
if (username && password.toLowerCase().includes(username.toLowerCase())) {
errors.push('Password cannot contain username');
}
// Calculate entropy (simplified)
const entropy = this.calculateEntropy(password);
if (entropy < this.minEntropy) {
errors.push('Password is too predictable');
}
// NIST: NO composition rules (uppercase, special chars) required!
// Users create stronger passwords without arbitrary rules
return {
valid: errors.length === 0,
errors,
entropy,
strength: this.getStrength(entropy)
};
}
calculateEntropy(password) {
const charsets = [
{ regex: /[a-z]/, size: 26 },
{ regex: /[A-Z]/, size: 26 },
{ regex: /[0-9]/, size: 10 },
{ regex: /[^a-zA-Z0-9]/, size: 32 }
];
const poolSize = charsets
.filter(cs => cs.regex.test(password))
.reduce((sum, cs) => sum + cs.size, 0);
return password.length * Math.log2(poolSize);
}
getStrength(entropy) {
if (entropy < 20) return 'Very Weak';
if (entropy < 40) return 'Weak';
if (entropy < 60) return 'Fair';
if (entropy < 80) return 'Strong';
return 'Very Strong';
}
}
// Secure password hashing
const argon2 = require('argon2');
async function hashPassword(password) {
// Argon2id: Best for password hashing (better than bcrypt)
return await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 2 ** 16, // 64 MB
timeCost: 3, // iterations
parallelism: 1,
hashLength: 32
});
// Time: ~100-200ms on modern hardware
}
Session Management
- Regenerate session IDs after login
- Implement absolute and idle timeouts
- Clear sessions on logout
- Monitor concurrent sessions
Common Vulnerabilities
1. Broken Authentication (OWASP #7)
Common Attack Vectors:
- Credential stuffing (automated login with breached passwords)
- Brute force attacks
- Session fixation
- Default credentials
Comprehensive Prevention:
// Multi-layered authentication security
const rateLimit = require('express-rate-limit');
const MongoStore = require('rate-limit-mongo');
// 1. Progressive rate limiting
const loginLimiter = rateLimit({
store: new MongoStore({
uri: process.env.MONGODB_URI,
collectionName: 'loginAttempts'
}),
windowMs: 15 * 60 * 1000,
max: async (req) => {
// Progressive delays: 5, 3, 1 attempts
const attempts = await getFailedAttempts(req.ip);
if (attempts > 10) return 1;
if (attempts > 5) return 3;
return 5;
},
skipSuccessfulRequests: true,
keyGenerator: (req) => {
// Rate limit by IP + username combo
return `${req.ip}:${req.body.username}`;
}
});
// 2. Account lockout mechanism
class AccountLockout {
async checkLockout(username) {
const user = await User.findOne({ username });
if (user.lockoutUntil && user.lockoutUntil > Date.now()) {
const remainingTime = Math.ceil(
(user.lockoutUntil - Date.now()) / 1000
);
throw new Error(`Account locked. Try again in ${remainingTime}s`);
}
// Reset if lockout expired
if (user.lockoutUntil && user.lockoutUntil <= Date.now()) {
await User.updateOne(
{ username },
{ $unset: { lockoutUntil: 1 }, $set: { failedAttempts: 0 } }
);
}
}
async recordFailure(username) {
const result = await User.findOneAndUpdate(
{ username },
{
$inc: { failedAttempts: 1 },
$set: { lastFailedAttempt: Date.now() }
},
{ new: true }
);
// Lock after 5 failures
if (result.failedAttempts >= 5) {
await User.updateOne(
{ username },
{
$set: {
lockoutUntil: Date.now() + (30 * 60 * 1000) // 30 min
}
}
);
}
}
}
// 3. CAPTCHA after failures
app.post('/login',
loginLimiter,
conditionalCaptcha, // Require CAPTCHA after 3 attempts
async (req, res) => {
try {
await lockout.checkLockout(req.body.username);
// ... authentication logic
} catch (error) {
await lockout.recordFailure(req.body.username);
throw error;
}
}
);
2. Broken Access Control
- Insecure direct object references
- Missing function level access control
- CORS misconfiguration
Prevention:
// Object level authorization
async function getDocument(userId, docId) {
const doc = await Document.findById(docId);
if (!doc || doc.ownerId !== userId) {
throw new Error('Unauthorized');
}
return doc;
}
3. JWT Vulnerabilities
- Algorithm confusion (RS256 → HS256)
- Weak secrets
- No expiration
- Sensitive data in payload
Prevention:
// Secure JWT verification
const jwt = require('jsonwebtoken');
function verifyToken(token) {
return jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'], // Explicitly set algorithm
maxAge: '1h' // Enforce expiration
});
}
Advanced Topics for Senior Roles
Zero Trust Architecture Implementation
// Zero Trust access decision engine
class ZeroTrustEngine {
async evaluateAccess(context) {
const scores = {
identity: await this.scoreIdentity(context.user),
device: await this.scoreDevice(context.device),
network: await this.scoreNetwork(context.ip),
behavior: await this.scoreBehavior(context.user, context.action),
resource: await this.scoreResourceSensitivity(context.resource)
};
const totalScore = Object.values(scores)
.reduce((sum, score) => sum + score, 0) / 5;
// Adaptive access based on risk score
if (totalScore < 30) return { decision: 'deny' };
if (totalScore < 60) return {
decision: 'challenge',
requirements: ['mfa', 'device_verification']
};
if (totalScore < 80) return {
decision: 'allow',
restrictions: ['read_only', 'audit_log']
};
return { decision: 'allow', restrictions: [] };
}
async scoreIdentity(user) {
let score = 100;
if (!user.mfaEnabled) score -= 30;
if (user.recentPasswordChange) score -= 10;
if (user.privilegedAccount) score -= 20;
return Math.max(0, score);
}
async scoreDevice(device) {
let score = 100;
if (!device.managed) score -= 40;
if (!device.encrypted) score -= 30;
if (device.jailbroken) score -= 50;
if (device.outdatedOS) score -= 20;
return Math.max(0, score);
}
// ... other scoring methods
}
Passwordless Authentication (The Future)
1. WebAuthn/FIDO2 Implementation
// Registration ceremony
async function registerWebAuthn(user) {
// 1. Generate challenge
const challenge = crypto.randomBytes(32);
// 2. Create credential options
const credentialOptions = {
challenge,
rp: {
name: 'MyApp',
id: 'myapp.com'
},
user: {
id: Buffer.from(user.id),
name: user.email,
displayName: user.name
},
pubKeyCredParams: [
{ alg: -7, type: 'public-key' }, // ES256
{ alg: -257, type: 'public-key' } // RS256
],
authenticatorSelection: {
authenticatorAttachment: 'platform', // Built-in
userVerification: 'required',
residentKey: 'required' // Passwordless
},
attestation: 'direct' // Get attestation for high security
};
// 3. Create credential on client
const credential = await navigator.credentials.create({
publicKey: credentialOptions
});
// 4. Verify and store public key
const verified = await verifyCredential(credential, challenge);
if (verified) {
await storePublicKey(user.id, credential.publicKey);
}
}
// Authentication ceremony
async function authenticateWebAuthn(username) {
// 1. Get user's credentials
const credentials = await getStoredCredentials(username);
// 2. Generate challenge
const challenge = crypto.randomBytes(32);
// 3. Request assertion
const assertion = await navigator.credentials.get({
publicKey: {
challenge,
allowCredentials: credentials.map(c => ({
id: c.credentialId,
type: 'public-key'
})),
userVerification: 'required'
}
});
// 4. Verify signature
return verifyAssertion(assertion, challenge);
}
2. Magic Link Security
// Secure magic link implementation
class MagicLinkAuth {
async sendMagicLink(email) {
// Generate cryptographically secure token
const token = crypto.randomBytes(32).toString('hex');
const hashedToken = await argon2.hash(token);
// Store with expiration
await redis.setex(
`magic:${email}`,
300, // 5 minutes
JSON.stringify({
hashedToken,
attempts: 0,
createdAt: Date.now()
})
);
// Send email with signed URL
const signedUrl = this.createSignedUrl(email, token);
await sendEmail(email, signedUrl);
}
createSignedUrl(email, token) {
const payload = { email, token };
const signature = crypto
.createHmac('sha256', process.env.URL_SECRET)
.update(JSON.stringify(payload))
.digest('hex');
return `https://myapp.com/auth/magic?
email=${email}&token=${token}&sig=${signature}`;
}
}
// WebAuthn registration example
const credential = await navigator.credentials.create({
publicKey: {
challenge: new Uint8Array(32),
rp: { name: "Example Corp" },
user: {
id: new TextEncoder().encode(userId),
name: userEmail,
displayName: userName
},
pubKeyCredParams: [{ alg: -7, type: "public-key" }],
authenticatorSelection: {
authenticatorAttachment: "platform"
}
}
});
API Security
- API Keys: For service-to-service
- OAuth 2.0: For user delegation
- Mutual TLS: Certificate-based auth
- HMAC: Request signing
// HMAC request signing
const crypto = require('crypto');
function signRequest(method, path, body, secret) {
const timestamp = Date.now();
const message = `${method}:${path}:${timestamp}:${body}`;
const signature = crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
return {
'X-Timestamp': timestamp,
'X-Signature': signature
};
}
Interview Tips
Common Questions
"Explain the OAuth 2.0 flow"
- Draw the diagram
- Explain each step
- Mention security considerations
"How would you store passwords?"
- Never plain text
- Use bcrypt/scrypt/Argon2
- Salt + hash
- Explain rainbow tables
"Design a permission system"
- Start with requirements
- Choose RBAC vs ABAC
- Consider scalability
- Discuss caching
"How do you prevent CSRF?"
- CSRF tokens
- SameSite cookies
- Double submit cookies
- Origin validation
Design Considerations
- Scalability: Token validation, session storage
- Performance: Caching permissions, JWT vs sessions
- User Experience: SSO, remember me, passwordless
- Compliance: GDPR, data residency, audit logs
Red Flags to Avoid
- Storing passwords in plain text
- Using MD5/SHA1 for passwords
- Client-side only validation
- Security through obscurity
- Ignoring OWASP guidelines
Production Tools & Libraries
Authentication Libraries Comparison
| Library | Use Case | Pros | Cons |
|---|---|---|---|
| Passport.js | Multi-strategy auth | 500+ strategies | Complex, heavyweight |
| Auth0 | Managed auth service | Full-featured, compliant | Cost, vendor lock-in |
| Firebase Auth | Quick setup | Google backing, easy | Limited customization |
| Supabase Auth | Open-source alternative | Self-hostable | Newer, smaller community |
| NextAuth.js | Next.js apps | Great DX, many providers | Next.js specific |
Security Libraries (Node.js)
// Password hashing (ranked by security)
argon2 // 🥇 Best: memory-hard, resistant to GPU attacks
bcrypt // 🥈 Good: widely supported, battle-tested
scrypt // 🥉 Good: memory-hard, in Node.js core
pbkdf2 // Acceptable: in Node.js core, NIST approved
// Session management
express-session // Server-side sessions
connect-redis // Redis session store
connect-mongo // MongoDB session store
// Token management
jsonwebtoken // JWT creation/verification
jose // Complete JOSE suite (JWS, JWE, JWK)
passcode // OTP/TOTP generation
// Security middleware
helmet // Security headers
cors // CORS management
express-rate-limit // Rate limiting
Testing Security
- OWASP ZAP: Security scanning
- Burp Suite: Penetration testing
- jwt.io: JWT debugging
- haveibeenpwned: Compromised password checking
Quick Reference
HTTP Status Codes
- 401 Unauthorized: Invalid authentication
- 403 Forbidden: Invalid authorization
- 429 Too Many Requests: Rate limiting
Security Headers
// Essential security headers
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Strict-Transport-Security', 'max-age=31536000');
next();
});
Cookie Attributes
- Secure: HTTPS only
- HttpOnly: No JavaScript access
- SameSite: CSRF protection
- Max-Age/Expires: Lifetime
- Domain/Path: Scope
Interview Success Checklist
Must-Know Concepts
| Concept | Key Points | Why It Matters |
|---|---|---|
| Authentication vs Authorization | AuthN = identity, AuthZ = permissions | Foundation question in every interview |
| OAuth 2.0 flows | Authorization Code + PKCE is current best practice | Most common in modern applications |
| JWT structure | Header.Payload.Signature, stateless tokens | Popular but understand the trade-offs |
| Password hashing | bcrypt/Argon2/scrypt, never reversible | Shows understanding of one-way functions |
| MFA types | Something you know/have/are | Critical for modern security |
| Session vs Token | Stateful vs stateless, revocation differences | Architecture decision point |
| RBAC vs ABAC | Role-based vs attribute-based access | Shows depth of authorization knowledge |
| Security headers | CSP, HSTS, X-Frame-Options, SameSite | Practical security implementation |
| Common attacks | CSRF, XSS, session fixation, replay | Demonstrates threat awareness |
| Zero Trust | Never trust, always verify | Modern security principle |
Power Answers for Common Questions
Q: "How do you prevent brute force attacks?"
A: "Layer defenses: rate limiting by IP and username, progressive delays, account lockout after failures, CAPTCHA after attempts, anomaly detection, and monitoring with alerting."
Q: "JWT vs Sessions?"
A: "JWTs are stateless, scale horizontally, work across domains, but can't be revoked. Sessions are stateful, easy to revoke, more secure for sensitive apps, but require sticky sessions in distributed systems. Choose based on requirements."
Q: "How do you implement Remember Me securely?"
A: "Issue a separate, long-lived refresh token stored in httpOnly cookie, rotate on use, tie to device fingerprint, allow users to view/revoke devices, and never extend session token lifetime."
Red Flags to Avoid
❌ Saying "OAuth is for authentication" (it's authorization)
❌ Storing passwords with reversible encryption
❌ Putting JWTs in localStorage
❌ Not mentioning rate limiting
❌ Ignoring the principle of least privilege
❌ Claiming sessions or JWTs are always better
Time Complexity Awareness
- Password hashing: O(2^cost) - intentionally slow
- Permission check: O(1) with hashmap, O(n) with list
- Token validation: O(1) for HMAC, O(1) for RSA verify
- Rate limiting: O(1) with sliding window