Security is fundamental to API design. Before we implement authentication in our Express API, we need to understand three critical concepts that are often confused: Identification, Authentication, and Authorization. Each serves a different purpose in securing your application.
<aside> 🔑
The Three Pillars of API Security:
Identification → "Who claims to be accessing?"
Authentication → "Can you prove who you are?"
Authorization → "Are you allowed to do this?"
</aside>
Identification is simply the claim of identity - providing a username, email, or user ID.
When you walk up to a hotel front desk and say "I'm John Smith," that's identification. You're claiming to be John Smith, but you haven't proven it yet.
// User provides identification
POST /api/auth/login
{
"email": "[[email protected]](<mailto:[email protected]>)", // ⬅ This is identification
"password": "secretpassword"
}
// Or via URL parameter
GET /api/users/john-smith // Username as identification
// Or via JWT token payload
{
"id": "user-123", // ⬅ User identification
"email": "[[email protected]](<mailto:[email protected]>)",
"username": "johnsmith"
}
<aside> ⚠️
Important: Identification alone is not secure. Anyone can claim to be anyone. It's just the starting point.
</aside>
Authentication is the process of verifying that the claimed identity is legitimate.
When you show your driver's license or passport at the hotel, that's authentication. You're proving you are who you claim to be.
// User proves identity with password
const isAuthenticated = await [bcrypt.compare](<http://bcrypt.compare>)(
providedPassword,
storedHashedPassword
)
if (isAuthenticated) {
// Generate session token
const token = jwt.sign({ userId: [user.id](<http://user.id>) })
}
// User proves identity with JWT
const authenticateToken = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1]
try {
const decoded = jwt.verify(token, SECRET)
req.user = decoded
next()
} catch {
res.status(401).json({ error: 'Invalid token' })
}
}