FASTAPI / 5. JWT AUTHENTICATION
JWT Authentication — Signup, Login, Protected Routes
Stateless authentication with JSON Web Tokens and bcrypt
EXPLANATION
JWT (JSON Web Token) is the standard for stateless authentication in REST APIs. No sessions, no server-side state — the token itself carries the user's identity.
How JWT auth works end-to-end:
① User sends POST /auth/token with username + password (form data)
② Server verifies password against bcrypt hash in DB
③ Server creates a JWT: header.payload.signature (base64 encoded, dot separated)
Payload contains: {"sub": "username", "exp": timestamp}
Signature: HMAC-SHA256(header + payload, SECRET_KEY) — cannot be forged
④ Server returns {"access_token": "...", "token_type": "bearer"}
⑤ Client sends every subsequent request with: Authorization: Bearer <token>
⑥ Server verifies signature + checks expiry → extracts username → fetches user from DB
Why it's stateless: the server never stores the token. It only stores SECRET_KEY. Any server that knows SECRET_KEY can verify any token — perfect for horizontal scaling.
Security rules you must follow:
• Store SECRET_KEY in environment variables, never in code
• Set short expiry (15–60 minutes for access tokens)
• Use HTTPS always — tokens in transit must be encrypted
• Never store tokens in localStorage (XSS risk) — use httpOnly cookies in production
• Hash passwords with bcrypt — it's intentionally slow to resist brute force
The OAuth2PasswordBearer scheme tells FastAPI to expect a Bearer token in the Authorization header. It adds a padlock icon to /docs automatically.ARCHITECTURE
SIGNUP FLOW:
POST /auth/signup { username, password }
↓
hash_password(password) → bcrypt hash
↓
Save user to DB with hashed password
↓
Return { message: "User created" }
LOGIN FLOW:
POST /auth/token { username, password } (form data)
↓
Fetch user from DB by username
↓
verify_password(plain, hash) → True/False
↓
create_access_token({"sub": username, "exp": now+15min})
↓
Return { access_token: "eyJ...", token_type: "bearer" }
PROTECTED ROUTE:
GET /me Authorization: Bearer eyJ...
↓
Extract token → decode with SECRET_KEY
↓
Check exp not expired → get "sub" → fetch user
↓
Inject user into route functionCODE