Skip to main content
Authentication answers: “Who are you?” This page builds a complete auth system using JWTs backed by MySQL, using the same callback pattern as 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:
The payload is base64 encoded, NOT encrypted. Anyone can decode it. Never put passwords or sensitive data in the payload.

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

Using it on a protected route:

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

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.
Return the same message (‘Invalid credentials’) for both cases. Different messages let attackers enumerate which emails are registered.
Always verify the password matches before signing a token. Never sign a token just because the user was found.
Use SELECT only for the columns you need. Never SELECT * and return the whole results object — it includes the hashed password.