Full-Stack Authentication: JWT, Sessions, and Security Best Practices
A complete guide to implementing secure authentication in full-stack applications using JWT, sessions, and industry-standard security practices.
Authentication Fundamentals
Authentication is the backbone of any web application. Modern full-stack applications typically use either JWT (JSON Web Tokens) or session-based authentication.
JWT Authentication
How JWTs Work
JSON Web Tokens consist of three parts: header, payload, and signature. They are stateless, meaning the server doesn't need to store session data.
`typescript
import jwt from 'jsonwebtoken'
function generateToken(userId: string): string {
return jwt.sign(
{ userId },
process.env.JWT_SECRET!,
{ expiresIn: '7d' }
)
}
`
Implementing JWT Middleware
`typescript
function authenticateToken(req: Request, res: Response, next: NextFunction) {
const token = req.headers.authorization?.split(' ')[1]
if (!token) {
return res.status(401).json({ error: 'Unauthorized' })
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET!)
req.user = decoded
next()
} catch {
return res.status(403).json({ error: 'Invalid token' })
}
}
`
Session-Based Authentication
Server Setup
`typescript
import session from 'express-session'
app.use(session({
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
},
}))
`
Security Best Practices
1. Hash passwords with bcrypt (never store plain text) 2. Use HTTPS in production 3. Implement rate limiting to prevent brute force 4. Set secure cookie flags (HttpOnly, Secure, SameSite) 5. Validate input on both client and server 6. Use refresh tokens for long-lived sessions 7. Implement CSRF protection
Conclusion
Choose JWT for stateless, scalable APIs and sessions for server-controlled authentication. Always prioritize security best practices regardless of your choice.