Node.js Authentication: Complete Theory & Practice Guide
Understand sessions, JWT, OAuth, and production-grade authentication security patterns.
SessionsJWTOAuth
Table of Contents
- Theory: Authentication Fundamentals
- Theory: Session-Based Authentication
- Theory: JWT Authentication
- Theory: OAuth 2.0 & Social Login
- Theory: Password Security
- Basic: Local Authentication
- Basic: JWT Implementation
- Advanced: OAuth with Passport
- Best Practices & Security
- Interview Q&A + MCQ
- Contextual Learning Links
1. Theory: Authentication Fundamentals
┌─────────────────────────────────────────────────────────────┐
│ Security Concepts │
├─────────────────────────────────────────────────────────────┤
│ Authentication: "WHO are you?" │
│ • Proving identity (login/password/biometric) │
│ Authorization: "WHAT can you do?" │
│ • Permissions and access levels │
│ Example: │
│ • Authentication: Login with email/password │
│ • Authorization: Admin can delete users │
└─────────────────────────────────────────────────────────────┘
| Factor | Examples | Security Level |
|---|---|---|
| Something you KNOW | Password, PIN | Low |
| Something you HAVE | Phone, token | Medium |
| Something you ARE | Fingerprint, face ID | High |
| Something you DO | Voice/signature pattern | Medium |
| Somewhere you ARE | GPS, IP geofence | Low |
const mfaTheory = {
description: 'Two or more different authentication factors',
commonImplementations: [
'Password + SMS code',
'Password + TOTP app',
'Password + biometric'
],
protectsAgainst: ['Password theft', 'Credential stuffing', 'Phishing attacks']
};
const authFlows = {
SPA: {
flow: ['Login', 'Server returns JWT', 'Client stores token', 'Send Authorization header'],
pros: 'Stateless and scalable',
cons: 'Token storage concerns'
},
SSR: {
flow: ['Login', 'Server session created', 'HTTP-only cookie set'],
pros: 'Strong cookie security model',
cons: 'Stateful session storage required'
},
mobile: {
flow: ['Login', 'Access + refresh tokens', 'Secure storage', 'Refresh cycle'],
pros: 'Good UX',
cons: 'Token lifecycle complexity'
}
};
2. Theory: Session-Based Authentication
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ User │ │ Client │ │ Server │ │ Store │
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
│ Login request │ │ │
│──────────────►│──────────────►│ Create session│
│ │ │──────────────►│
│ │ Set cookie sid│ │
│ │◄──────────────│ │
│ Next request │ │ Lookup session│
│──────────────►│──────────────►│──────────────►│
└───────────────────────────────────────────────────────────────┘
const sessionStorageOptions = {
'In-Memory': { pros: ['Fast', 'Simple'], cons: ['Lost on restart', 'Not scalable'], useCase: 'Development' },
'Redis': { pros: ['Persistent', 'Fast', 'Scalable'], cons: ['Extra infra'], useCase: 'Production' },
'Database': { pros: ['Persistent'], cons: ['Slower than Redis'], useCase: 'Small apps' },
'Memcached': { pros: ['Very fast'], cons: ['No persistence'], useCase: 'Caching-heavy workloads' }
};
| Attribute | Description | Security Impact |
|---|---|---|
| HttpOnly | Blocks JavaScript access | Critical (XSS mitigation) |
| Secure | Sent only over HTTPS | Critical (MITM mitigation) |
| SameSite | Cross-site send rules | CSRF mitigation |
| MaxAge/Expires | Cookie lifetime | Limits token window |
| Domain/Path | Scope control | Reduces exposure surface |
3. Theory: JWT Authentication
┌─────────────────────────────────────────────────────────────┐
│ JWT Token │
├─────────────────────────────────────────────────────────────┤
│ xxxxx.yyyyy.zzzzz │
│ Header . Payload . Signature │
│ Header: {"alg":"HS256","typ":"JWT"} │
│ Payload: {"sub":"123","name":"John","iat":...,"exp":...} │
└─────────────────────────────────────────────────────────────┘
| Claim | Name | Description | Required? |
|---|---|---|---|
| iss | Issuer | Token issuer | Optional |
| sub | Subject | User identifier | Recommended |
| aud | Audience | Intended recipient | Optional |
| exp | Expiration | Expiry timestamp | Recommended |
| iat | Issued At | Creation timestamp | Recommended |
| jti | JWT ID | Token unique identifier | Optional |
| Aspect | JWT | Session |
|---|---|---|
| Storage | Client-side | Server-side store |
| Statelessness | Yes | No |
| Scalability | Excellent | Needs shared session store |
| Invalidation | Hard until expiry | Easy (delete session) |
| Best fit | APIs/SPAs/mobile | Traditional SSR apps |
const refreshTokenFlow = {
flow: [
'Login returns access + refresh tokens',
'Access token used for requests',
'On expiry, send refresh token',
'Server validates and rotates tokens'
],
benefits: [
'Limits impact of access token theft',
'Supports refresh token revocation',
'Avoids storing credentials on device'
]
};
4. Theory: OAuth 2.0 & Social Login
┌─────────────────────────────────────────────────────────────┐
│ OAuth 2.0 Roles │
├─────────────────────────────────────────────────────────────┤
│ Resource Owner (User) authorizes Client (Your App) │
│ Client exchanges auth code for token from Authorization srv │
│ Client uses token to access Resource server user data │
└─────────────────────────────────────────────────────────────┘
| Grant Type | Use Case | Security Level |
|---|---|---|
| Authorization Code | Server-side web apps | High |
| Authorization Code + PKCE | Mobile/SPA | High |
| Client Credentials | Machine-to-machine | High |
| Password Grant | Legacy trusted apps | Low |
| Implicit | Deprecated | Low |
const oauthProviders = {
Google: {
authUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
tokenUrl: 'https://oauth2.googleapis.com/token',
userInfoUrl: 'https://www.googleapis.com/oauth2/v2/userinfo',
scopes: ['email', 'profile']
},
GitHub: {
authUrl: 'https://github.com/login/oauth/authorize',
tokenUrl: 'https://github.com/login/oauth/access_token',
userInfoUrl: 'https://api.github.com/user',
scopes: ['user:email']
}
};
5. Theory: Password Security
| Algorithm | Speed | Security | Recommendation |
|---|---|---|---|
| bcrypt | Slow (configurable) | High | Recommended |
| scrypt | Very slow | Very high | Good |
| argon2 | Slow | Highest | Excellent |
| PBKDF2 | Configurable | Moderate | Acceptable legacy |
| MD5/SHA1 | Fast | Broken | Never use |
const bcryptComplexity = {
definition: 'Rounds = 2^workFactor',
recommendation: { current: '10-12', highSecurity: '12-14' }
};
const passwordResetFlow = {
flow: [
'Generate cryptographically random reset token',
'Store token hash + expiry',
'Email reset link',
'Verify token and expiry',
'Set new password',
'Invalidate token and sessions'
],
securityMeasures: [
'Rate limit reset requests',
'Avoid revealing whether email exists',
'Expire/reset token after short window'
]
};
const lockoutStrategies = {
progressiveDelay: '1s → 5s → 30s → 5min',
accountLockout: 'Lock after repeated failures',
captchaAfterFailures: 'Add captcha after threshold'
};
6. Basic: Local Authentication
npm install express-session connect-redis redis
// session-config.js
const sessionConfig = {
store: new RedisStore({ client: redisClient }),
name: 'sessionId',
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 1000 * 60 * 60 * 24
}
};
// local auth highlights
router.post('/register', validateRegister, async (req, res) => {
const hashedPassword = await bcrypt.hash(req.body.password, 12);
// save user + create session
});
router.post('/login', loginLimiter, async (req, res) => {
// validate user + bcrypt.compare + create session
});
router.post('/logout', (req, res) => {
req.session.destroy(() => { res.clearCookie('sessionId'); res.json({ message: 'Logout successful' }); });
});
7. Basic: JWT Implementation
npm install jsonwebtoken bcrypt
// jwt-auth.js
const ACCESS_TOKEN_EXPIRY = '15m';
const REFRESH_TOKEN_EXPIRY = '7d';
function generateTokens(userId, role) {
const payload = { sub: userId, role };
const accessToken = jwt.sign(payload, JWT_SECRET, { expiresIn: ACCESS_TOKEN_EXPIRY });
const refreshToken = jwt.sign(payload, JWT_SECRET, { expiresIn: REFRESH_TOKEN_EXPIRY });
return { accessToken, refreshToken };
}
function authenticateJWT(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) return res.status(401).json({ error: 'No token provided' });
try {
const decoded = jwt.verify(authHeader.split(' ')[1], JWT_SECRET);
req.user = { id: decoded.sub, role: decoded.role };
next();
} catch {
return res.status(403).json({ error: 'Invalid token' });
}
}
// refresh flow
router.post('/jwt/refresh', (req, res) => {
const { refreshToken } = req.body;
// validate stored refresh token, rotate, return new pair
});
8. Advanced: OAuth with Passport
npm install passport passport-google-oauth20 passport-github2 express-session
// passport-config.js
passport.serializeUser((user, done) => done(null, user.id));
passport.deserializeUser(async (id, done) => done(null, await User.findById(id)));
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/auth/google/callback'
}, async (accessToken, refreshToken, profile, done) => {
// find or create user
done(null, user);
}));
router.get('/auth/google', passport.authenticate('google'));
router.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
(req, res) => res.redirect(`/auth-success?token=${generateJWT(req.user)}`)
);
// GitHub strategy similar pattern
passport.use(new GitHubStrategy({
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: '/auth/github/callback'
}, async (accessToken, refreshToken, profile, done) => done(null, user)));
9. Best Practices & Security
// security headers
app.use(helmet({
hsts: { maxAge: 31536000, includeSubDomains: true, preload: true }
}));
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
next();
});
// csrf
const csrfProtection = csrf({
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict'
}
});
app.use('/api', csrfProtection);
// secure password reset
const resetToken = crypto.randomBytes(32).toString('hex');
const resetTokenHash = crypto.createHash('sha256').update(resetToken).digest('hex');
// store hash + expiry, email raw token
// on use: hash incoming token and compare against stored hash
const securityChecklist = {
passwords: ['Hash with bcrypt', 'Strong policy', 'No password logging'],
sessions: ['HTTP-only/Secure/SameSite', 'session timeout', 'invalidate on logout'],
jwt: ['Short-lived access token', 'refresh rotation', 'algorithm validation'],
general: ['Rate limiting', 'HTTPS', 'security logs', 'CSRF tokens', 'Helmet']
};
| Attack | Description | Mitigation |
|---|---|---|
| Brute Force | Repeated password guesses | Rate limit, CAPTCHA, lockout |
| Credential Stuffing | Reused leaked credentials | MFA, breach checks |
| Session Fixation | Forced known session ID | Regenerate session on login |
| XSS | Injected script execution | HTTP-only cookies, CSP, sanitization |
| CSRF | Forged authenticated request | CSRF tokens + SameSite |
| MITM | Traffic interception | HTTPS, HSTS, secure cookies |
| Token Replay | Reuse stolen token | Short expiry, revocation, rotation |
10 Interview Questions + 10 MCQs
1Authentication vs authorization?easy
Answer: Authentication verifies identity; authorization controls allowed actions.
2Why use MFA?easy
Answer: It reduces account compromise risk even when password is stolen.
3When prefer sessions over JWT?medium
Answer: SSR apps needing strict cookie-based security and easy server-side invalidation.
4When prefer JWT?medium
Answer: APIs/SPAs/mobile where stateless auth and distributed verification are useful.
5Why avoid plain JWT long lifetime?medium
Answer: Stolen tokens remain valid too long; use short access tokens + refresh flow.
6What makes password hashing secure?easy
Answer: Slow, salted, adaptive hashing (bcrypt/argon2/scrypt).
7Why `HttpOnly` on auth cookies?easy
Answer: Prevents JavaScript access, reducing token theft via XSS.
8OAuth authorization code flow advantage?hard
Answer: Better security by exchanging code server-side and protecting client secrets.
9How to defend login endpoint abuse?easy
Answer: Rate limiting, lockout policies, CAPTCHA, and suspicious-event monitoring.
10Why rotate refresh tokens?hard
Answer: Limits replay and provides stronger revocation semantics.
10 Authentication MCQs
1
Authentication answers:
Explanation: Authentication verifies identity.
2
Most recommended password hashing:
Explanation: Use adaptive slow hash algorithms with salts.
3
Cookie flag preventing JS access:
Explanation: HttpOnly blocks `document.cookie` access.
4
JWT stands for:
Explanation: JWT = JSON Web Token.
5
Best token strategy for APIs:
Explanation: Limits replay risk while preserving UX.
6
OAuth authorization code flow primarily uses:
Explanation: Authorization code is exchanged for token securely.
7
Main CSRF defense with forms:
Explanation: CSRF token validation stops forged cross-site requests.
8
Session-based auth is easier to invalidate because:
Explanation: Stateful storage allows immediate invalidation.
9
Best response for invalid login:
Explanation: Avoid user enumeration disclosures.
10
Critical production requirement:
Explanation: These are foundational controls for auth security.