Skip to main content

Command Palette

Search for a command to run...

Building and Breaking My Own JWT Auth System — Part 1: Setting the Foundation

Updated
•3 min read•View as Markdown
R
Cybersecurity graduate focused into privacy engineering and IAM. I write about the projects I build - what worked, what broke, and what I'd do differently. 👾

Part 1 of a series where I build a Flask API with JWT authentication, then deliberately exploit six real vulnerabilities in it — documenting why each one exists at the JWT specification level, not just in the code.

What this series is

JWTs are everywhere in modern authentication, and JWT misconfigurations are some of the most common — and most tested — vulnerabilities in identity and access management. Most write-ups on this topic show you the exploit and move on. This series does something slightly different: I'm building the auth system first, breaking it myself, and documenting why the spec allows the break before fixing it.

By the end of the series, one small Flask API will have been through:

  • alg: none signature bypass

  • Weak HMAC secret cracking (offline, via hashcat)

  • kid header parameter injection

  • JWKS URI spoofing

  • Token replay after expiry

  • RS256 → HS256 algorithm confusion

Each of these maps to a real, documented class of vulnerability that shows up repeatedly in JWT libraries and implementations in the wild — not contrived bugs.

Why build it instead of just reading about it

Reading a writeup of a JWT vulnerability and reproducing it yourself are two very different levels of understanding. If I can only explain an attack in the abstract, I don't actually understand where in the trust chain it breaks. Building the vulnerable version first — and watching my own token get forged — is a much sharper way to learn than starting from a hardened example and reading commentary on it.

Part 1: the boring (necessary) part — environment and foundation

Before any JWT code exists, this part covers the project's foundation: an isolated Python environment, a minimal Flask app, and git/GitHub set up correctly from the start.

This matters more than it looks like at first glance. A few decisions made here directly support the security reasoning later in the series:

  • A virtual environment isolates dependencies per-project. This becomes relevant again later when pinning exact library versions matters for reproducing a specific vulnerability — some JWT library CVEs are version-specific.

  • .gitignore excludes venv/ and a future .env file from day one, before any real secret exists in the project. Leaked secrets in public repos are themselves a well-known vulnerability class — the habit of gitignoring secrets before writing any code that generates them is deliberate, not an afterthought.

  • requirements.txt pins exact versions, so the environment is fully reproducible — important for anyone (including future me) trying to recreate a specific exploit later.

The first working piece of the API is just a single health-check route:

from flask import Flask

app = Flask(__name__)

@app.route("/health")
def health():
    return {"status": "ok"}

if __name__ == "__main__":
    app.run(debug=True)

Nothing security-relevant yet — just confirming the request/response cycle works end-to-end before adding authentication on top of it.

$ curl http://127.0.0.1:5000/health
{
  "status": "ok"
}

What's next

Part 2 builds the actual login flow and issues a first JWT — and that's where the interesting part of the series begins: understanding exactly what's inside a JWT (header, payload, signature) and why that structure is what makes every later attack possible.


Follow along — the full code for each stage is on GitHub.

JWT Attack Lab

Part 1 of 8

Building a Flask API with JWT authentication from scratch, then breaking it six different ways — alg none bypass, HMAC brute force, kid injection, JWKS spoofing, token replay, and RS256/HS256 confusion. Each attack fixed with an explanation of why it exists at the JWT spec level.

Up next

JWT Attack Lab — Part 2: What's Actually Inside a JWT (and Why It Matters Later)

Part 2 of a series building a Flask API with JWT authentication, then deliberately exploiting six real vulnerabilities in it. In Part 1, the project got its foundation — a Flask app, an isolated envir

More from this blog

Q

QuietBytes

23 posts