Skip to content
The Validation Procedure, In Order
step 1/5

Reading — step 1 of 5

Learn

~4 min readSecurity & Operations

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

#StepWhat you are holding when it starts
1split on . — three segmentstext a stranger sent you
2base64url-decode segment 0bytes a stranger sent you
3parse it: the JOSE headera stranger's account of how to check this
4is this algorithm one I accept?your configuration answering, not the header
5verify the signature over seg0.seg1the first outcome nobody else chose
6decode and parse segment 1now, and only now, claims worth reading
7check exp, nbf, iss, auda 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

python

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 exp before the signature "to fail fast". The cheap check tells you nothing, and you paid for it by trusting attacker input.
  • Reading alg and 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…

The Validation Procedure, In Order — Build a JWT Library