Reading — step 1 of 5
Learn
The Validation Procedure, In Order
You now hold every check this course teaches: three segments, base64url, a pinned
algorithm bound to a key type, an HMAC compared in constant time, kid resolved
against a registry you control, then exp, nbf, iss, aud. Listed like that
they are a checklist. Run in the wrong order, several do nothing at all.
What the RFC actually says
RFC 7519 §7.2 states validation as a numbered procedure and closes with a blunt rule: if any step fails, the JWT MUST be rejected. It also carries a caveat worth reading slowly — the order of the steps is not significant where there are no dependencies between their inputs and outputs. That is not a licence to shuffle; it points at where the real constraint lives. You cannot choose an algorithm before decoding the header, cannot verify before choosing a key, and the claims are worth nothing until the signature passes, so anything you do with them earlier you do on the attacker's behalf.
The pipeline
| # | Step | What you are holding when it starts |
|---|---|---|
| 1 | split on . — three segments | text a stranger sent you |
| 2 | base64url-decode segment 0 | bytes a stranger sent you |
| 3 | parse it: the JOSE header | a stranger's account of how to check this |
| 4 | is this algorithm one I accept? | your configuration answering, not the header |
| 5 | verify the signature over seg0.seg1 | the first outcome nobody else chose |
| 6 | decode and parse segment 1 | now, and only now, claims worth reading |
| 7 | check exp, nbf, iss, aud | a token, rather than a string |
The fence sits between 5 and 6: above it is input handling, below it is
authorization. Everything this course taught maps onto those rows — base64url at
2 and 6, alg: none and key-type mismatch and header-supplied keys all at 4,
kid lookup between 4 and 5, the constant-time HMAC at 5. §7.2 adds one thing
after its ten steps: even a cleanly validated JWT SHOULD be rejected if its
algorithm is not one you accept.
Reading claims early costs nothing and proves nothing
Every line ran, three segments were present, nothing was checked. If that sub
reaches a log line, a rate-limit bucket, a cache key or a metrics label before
step 5, an unauthenticated stranger has written into your systems — and if the
code goes on to use it, they have logged in.
Reading the header early is fine; deciding early is not
You have to read the header to verify at all: alg and kid live there. Note the
asymmetry. Reading kid to choose is safe, because a wrong choice ends in a
signature failure a step later; reading sub to decide is not, because
nothing downstream re-checks it. Libraries ship a decode-without-verifying call
for exactly that selection, and for debugging — it returns the same shape as the
verifying call, which is how it reaches production by accident.
Common mistakes
- Decoding the payload first because it is the interesting part. It is the attacker's part until step 5 says otherwise.
- Reporting the last failure instead of the first. Expired and wrongly signed is a forgery, not a stale session; filing it as expiry hides the attack.
- Checking
expbefore the signature "to fail fast". The cheap check tells you nothing, and you paid for it by trusting attacker input. - Reading
algand believing it. Step 4 exists to consult your configuration.
Your exercise
You will run tokens through that pipeline and print where each one stops, so the tests are built from tokens that fail more than one step.
The visible test feeds you the same expired payload twice — once with its real
signature, once with QUFBQQ in the signature segment. The first is
REJECT claims expired; the second is REJECT signature mismatch, because the
pipeline never reaches the claims. Validate claims first and both come back
expired, which looks reasonable and is wrong.
Two more. A token with alg: none and a perfectly readable payload is
REJECT alg not_allowed, because step 4 runs before anything is verified. And a
token whose signature is correct but whose payload is not JSON is
REJECT payload not_json, reachable only if you decode the payload after the
signature rather than before it.
You print a step and a reason because you are the operator reading the log. What
a real service returns is 401 and nothing else — a caller who can tell
signature mismatch from claims expired is being told which guess got further.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…