Skip to content
Lesson 2 of 14

Step 1 of 5 · Reading · ~3 min

Read

JWT Anatomy

Header & Payload

Two JSON objects, encoded and joined by a dot. The header says how the token is protected, the payload what is asserted. Keep that split straight and most JWT confusion goes away.

What belongs in the header?

{"alg":"HS256","typ":"JWT"}
ParameterWhat it means
algRequired. The algorithm that produced the signature: HS256, RS256, ES256, none.
typOptional. Media type of the whole token. RFC 7519 §5.1 recommends "JWT" and says JWT implementations ignore it — it exists so an app handed several kinds of object can tell them apart.
ctyOptional, and only for a nested JWT (one carried as the payload of another), where it MUST be "JWT" (§5.2).
kidOptional. Which key signed this, so a verifier holding several picks the right one.

There is no "typ":"JWE" — an encrypted JWT is still typed "JWT"; its five-segment structure makes it a JWE. And typ is not a security control; the spec tells implementations to ignore it.

The header is not protected until you check the signature — which is computed using the algorithm the header names. That circularity is the root of nearly every JWT vulnerability.

What belongs in the payload?

The payload is the JWT Claims Set: a flat JSON object of name/value pairs, each a statement about the subject. RFC 7519 sorts claim names into three classes, and the class says whether you may invent one.

ClassRuleExample
Registered (§4.1)The seven IANA-registry names. The RFC defines their meaning; all are OPTIONAL.iss sub aud exp nbf iat jti
Public (§4.2)IANA-registered, or collision-resistant — in practice a URI you control.https://api.parcelforge.com/tenant
Private (§4.3)A bare name you and your consumer agreed on.role, plan

The seven registered claims

ClaimNameMeaning
ississuerwho minted it
subsubjectwho it is about
audaudiencewho may accept it
expexpiration timereject at or after this instant
nbfnot beforereject before this instant
iatissued atwhen it was minted, so you can judge its age
jtiJWT IDunique id, so a replay can be spotted

exp, nbf and iat are NumericDate values: a JSON number of seconds since the Unix epoch. Not a string, not milliseconds. Emitting "exp":"2027-01-01T00:00:00Z" is the most common claims bug — compliant verifiers reject it, sloppy ones coerce it to 0 and decide every token expired in 1970.

Two rules the spec states that almost nobody implements

Claim names must be unique (§4) — a parser must reject a duplicate or keep only the lexically last. If one component keeps the first sub and another the last, one token authenticates as two different people.

Claims you do not understand must be ignored (§4) — and never assume one is present just because your issuer usually sends it.

Encode the bytes you were given

python

Same JSON value, two different segments — json.dumps adds a space after every : and ,. RFC 7519 §7.1 allows whitespace here and requires no canonicalization, so the exact bytes get signed. Re-serialize and you have built a different token.

✓  encode the bytes you received or were handed
✗  json.loads(...) then json.dumps(...) and encode that

Your exercise

You will read <header_json>|<payload_json> and print <base64url(header)>.<base64url(payload)>.

The hidden case feeds you {"typ":"JWT","alg":"RS256"}typ first, which no JSON library emits on its own. Parse and re-dump it and you produce eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiJ9 rather than eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9, failing on three extra spaces. Split on | and encode each half's bytes untouched.

Up nextHS256 SigningSigning & Verification

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…

Header & Payload — Build a JWT Library