db.query(). You will see register, login, and a verifyToken middleware that protects any route.
Authentication vs Authorization
Sessions vs JWT
What Is a JWT?
A JWT has three base64url parts separated by dots:MySQL Table
config/db.js
Install
Register Route
Login Route
This is the exact pattern from the course example:In production, store your JWT secret in
.env as process.env.JWT_SECRET instead of the hardcoded string 'secretKey'. A hardcoded secret is fine for learning but must never be used in a deployed application.verifyToken Middleware
How the Auth Flow Works
1
Client registers
POST /api/auth/register. Server checks duplicate, hashes password, INSERTs row, returns JWT.
2
Client logs in
POST /api/login with email + password. Server SELECTs user, compares hash with bcrypt.compare(), signs and returns JWT.
3
Client stores token
Client stores the JWT (localStorage for learning; httpOnly cookie for production).
4
Client sends token
Every request to a protected route includes
Authorization: Bearer TOKEN header.5
verifyToken runs
Middleware extracts token, calls jwt.verify(), attaches decoded payload to req.user, calls next().
6
Handler uses req.user
Access req.user.id, req.user.email, req.user.role for personalized responses.
JWT Claims in Payload
Key Terms
Common Mistakes
Using 'secretKey' in production
Using 'secretKey' in production
The hardcoded string ‘secretKey’ is the same for every application that copies the example. In production, always use a long random string from process.env.JWT_SECRET.
Same error for wrong email and wrong password
Same error for wrong email and wrong password
Return the same message (‘Invalid credentials’) for both cases. Different messages let attackers enumerate which emails are registered.
Not calling bcrypt.compare() before signing the token
Not calling bcrypt.compare() before signing the token
Always verify the password matches before signing a token. Never sign a token just because the user was found.
Returning password hash in the response
Returning password hash in the response
Use SELECT only for the columns you need. Never SELECT * and return the whole results object — it includes the hashed password.