Step 1 of 5 · Reading · ~4 min
Read
Claims & Validation
Standard Claim Validation
A valid signature tells you the token was not altered and came from someone holding the key. It says nothing about whether it is still good, or whether it was meant for you. Separate questions, asked in a strict order.
The rule that comes before every other rule
Check the signature first. Read no claim until it passes.
RFC 7519 §11.1: a JWT's contents cannot be relied on for a trust decision unless
cryptographically secured. Before verification the payload is a string a stranger
sent you — logging sub from it hands the attacker a result they chose.
The four checks, with their exact predicates
| Claim | Reject when | Meaning |
|---|---|---|
exp | now > exp + leeway | expired |
nbf | now < nbf - leeway | not valid yet |
iss | iss != expected_iss | someone else minted it |
aud | your id is not among the values | not meant for you |
Every one is OPTIONAL per the RFC — a token may carry none. Your policy is not:
decide whether a missing exp is acceptable and enforce it. Silence is not
permission.
Getting the expiry boundary right
RFC 7519 §4.1.4 requires the current time to be before exp, so by the spec
alone a token is dead the instant the clock reads exp itself. Leeway pushes that
edge outward, and this course treats the grace window as inclusive of its right
edge — the live boundary is:
Those two rules differ by exactly one second when leeway is 0: the spec would
call now == exp expired, the predicate below still calls it valid. With any real
leeway the distinction disappears, and every exercise here sets one. Pick a
convention, write it down, and apply it to nbf the same way — what breaks systems
is two services disagreeing about the edge, not which edge you chose.
The > does real work: at exactly exp + leeway the token is still inside the
grace window, dying a second later.
exp = 970 exp + leeway = 1000
| |
── valid ────────|──── grace (leeway 30) ───|──── expired ──>
|
now = 1000 still OK
now = 1001 EXPIRED
Write >= and the middle line flips to EXPIRED — a one-character bug visible
only at the boundary, which is exactly where the grader looks.
nbf is the mirror image, and its leeway points the other way — the token becomes
usable a little early — so you subtract: not-yet-valid when
now < nbf - leeway. With nbf 5000 and leeway 60 the token wakes up at 4940,
so 4939 is NOT_YET_VALID and 4940 is already OK.
Why leeway exists
Two servers never agree on the time. Clocks drift seconds to minutes apart, and a verifier running fast rejects tokens that were valid when issued. The RFC permits "some small leeway, usually no more than a few minutes"; thirty seconds to five minutes is normal. Do not reach for an hour — leeway is extra life for a token you wanted dead.
aud is a MUST, and it is usually skipped
RFC 7519 §4.1.3: if aud is present and the service processing it does not find
itself among the values, the JWT MUST be rejected. It is an array in general,
and may be a bare string for a single audience, so handle both:
Skip it and every token from your provider works against every one of your services — one minted for the analytics API is accepted by billing.
What "expired" has to do
Reject the request. Not "log them in as anonymous", not "accept and refresh server-side". Expiry is the only revocation a stateless JWT has; treat it as a soft warning and you have none.
Your exercise
You will fill in validate(), checking in the order exp, nbf, iss, aud
and returning the first failure.
The visible test sets NOW 1000 and LEEWAY 30, then validates "exp":970 and
expects OK. Since 970 + 30 == 1000 the token sits exactly on the boundary:
now > exp + leeway is false, so it survives. Write >= and you return EXPIRED,
fail that line, and fail the end-to-end exercise later for the same reason. The
nbf boundary is tested the same way — subtract the leeway there, do not add it.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…