Home / Blog / Encoding & Base64

Hash crypto & JWT

JWT Decode vs Verify: Reading Claims Does Not Establish Trust

Distinguish JWT decoding, signature verification, claim validation, and authorization, with a tampering example and clear local-tool limits.

Separate inspection from an authorization decision

Decoding a JWT makes its encoded header and claims readable. Signature verification checks cryptographic integrity with an appropriate trusted key and algorithm. Claim validation determines whether the token meets the receiving application's rules. Authorization then decides whether the authenticated subject may perform the requested operation.

These are separate stages. A neatly formatted payload containing role: admin is not proof that an administrator issued it. A token can also have a valid signature while being expired, intended for another API or insufficient for a particular action. A decoder should never become the access-control decision point.

See why readable claims are easy to change

const originalClaims = {sub: "demo-user", role: "reader"};
const changedClaims = {...originalClaims, role: "admin"};
const encode = value =>
  Buffer.from(JSON.stringify(value), "utf8").toString("base64url");

const firstPayload = encode(originalClaims);
const secondPayload = encode(changedClaims);
console.assert(firstPayload !== secondPayload);
console.assert(
  JSON.parse(Buffer.from(secondPayload, "base64url").toString("utf8")).role
    === "admin"
);

This Node.js example changes a payload segment without using any signing key. It does not produce a valid signed token. If that changed segment replaces the payload of an existing signed token while its original signature remains, a correctly configured verifier should reject the altered signed content.

The ease of reading the changed JSON is the point: encoding is a representation operation. It neither encrypts the claims nor makes them trustworthy. Do not place a secret in a signed JWT merely because its payload looks unreadable at first glance.

Choose verification rules before reading attacker-controlled hints

The receiving application needs a trusted issuer configuration, acceptable algorithms and suitable verification keys. Do not let a token's unverified header freely select any algorithm or remote key location. The JWT best-current-practices document discusses algorithm verification, key selection and preventing confusion between token uses.

A key identifier can help select among configured keys, but it is still input that needs controlled interpretation. It should not become an unchecked file path or arbitrary network URL. Use a maintained verification library appropriate to the issuer's documented profile rather than assembling a production verifier from an inspection snippet.

Apply the application's required claims after verification

Check the issuer and audience expected by this application, along with expiration and any not-before requirement. Validate the claim types your profile requires. A string that looks like a timestamp is not automatically equivalent to a numeric timestamp, and a missing required claim is different from a claim that happens to have a convenient default.

The JWT specification defines registered claims such as iss, aud, exp and nbf. Their presence and application-specific interpretation depend on the consuming profile. Use a deliberate clock-skew allowance if needed and test the exact expiration boundary rather than extending every token indefinitely.

Understand what the local tools establish

The JWT Decoder helps inspect a token's header and payload. It does not verify the signature. Use synthetic examples for screenshots and shared debugging: real tokens may contain personal information or provide access even when the tool processes them locally.

The separate JWT Validator can perform a local HS256 signature check when supplied with the corresponding secret and report selected timing issues. It does not replace an application's issuer, audience, revocation and permission policy. A signature result is evidence about that cryptographic check, not approval to access a resource.

Build tests that distinguish the stages

Keep one valid synthetic token and alter one payload claim while retaining its signature. Both payloads should remain readable, while the altered token fails verification. Repeat with the wrong verification key and with a correctly signed but expired token.

Finally test a correctly signed token for the wrong audience and a valid subject lacking the requested permission. Those cases demonstrate why signature verification alone is insufficient. Record which stage rejected each fixture so that a future refactor cannot accidentally replace the full policy with a decoder's successful parse.

Keep malformed JSON and invalid signature fixtures separate: a parser failure should not be reported as successful verification of an empty claims object.