DEV Community

Cover image for Every JWT debugger tells you not to paste real tokens. So I built one where it doesn't matter.

Every JWT debugger tells you not to paste real tokens. So I built one where it doesn't matter.

Mike on July 22, 2026

Go look at any online JWT tool. Somewhere on the page there's a line telling you not to paste production tokens. Nobody listens. The whole reason ...
Collapse
 
ofri-peretz profile image
Ofri Peretz

The algorithm-confusion refusal is the detail that stands out — I've seen that exact jwt.verify(token, publicKey, { algorithms: [header.alg] }) pattern in real codebases, almost always copy-pasted from a Stack Overflow answer nobody read past the first green checkmark. It's a clean target for a static-analysis rule too: flag any algorithms array whose value traces back to the token payload instead of a literal in the code. The beacon anecdote is honestly the better lesson though — "trust me, nothing is sent" is a claim, and the only way to verify a claim is to watch the wire yourself, same as I'd tell anyone reviewing AI-generated code not to trust the docstring over the diff.

Collapse
 
mike_0f35516d33902a0d2546 profile image
Mike

The static-analysis idea is good - "algorithms array traces back to the token" is exactly the taint pattern. And yes, the beacon lesson generalizes: the claim is never the artifact, the wire is.

Collapse
 
wrencalloway profile image
Wren Calloway

The two-verdict split is the right call, but there's a third row hiding inside your "Time" row that bites people harder than expiry: nbf failures caused by clock skew, not by a genuinely-not-yet-valid token. A token minted on a server whose clock is 40 seconds ahead of the validator reads as "not yet valid" for those 40 seconds, and it looks identical to a config bug. Most production JWT libraries default to a leeway/clock-tolerance window precisely because of this, and a lot of people don't know it exists until they trip over it. If your Time row just says pass/fail against the raw exp/nbf, consider showing the actual delta — "expired 8s ago" or "nbf in the future by 31s" — because "how far off" is exactly the signal that tells you skew from a real staleness problem.

One thing I'd verify rather than assume: your alg-confusion refusal is correct for the HS256-with-public-key case, but the nastier variant is when someone genuinely runs a mixed HS/RS setup and legitimately passes a shared secret. Make sure the refusal is keyed on "you handed me a public key for an HMAC alg," not just "the header says HS256," or you'll false-positive on the people who actually have a valid reason to be there.

Collapse
 
mike_0f35516d33902a0d2546 profile image
Mike

Update: shipped. Turns out the deltas were already in the verdicts ("EXPIRED 3 minutes ago"), but your skew point was the real gap - deltas under ~90 seconds now explicitly say they're likely clock skew and that most verifiers allow leeway. Live now, with tests. Thank you again for making the tool better.

Collapse
 
wrencalloway profile image
Wren Calloway

No problem, glad I could help. Take care!!

Collapse
 
mike_0f35516d33902a0d2546 profile image
Mike

Both points are well taken. Showing the delta ("expired 8s ago" / "nbf 31s in the future") is a genuinely better design than pass/fail and I'm going to add it; you're right that the magnitude is what distinguishes skew from staleness.

On the refusal: it's keyed on the key material, not the header - a shared secret with an HS256 token verifies normally; the refusal only fires when you hand asymmetric public key material to an HMAC alg. Mixed HS/RS setups work.

Collapse
 
frank_signorini profile image
Frank

This is awesome! How does it actually not matter if you paste real tokens? Is it

Collapse
 
mike_0f35516d33902a0d2546 profile image
Mike

Thanks! It's all WebCrypto in the browser - decoding, signature verification, key parsing, generation. There's no backend to send anything to.

The way to check rather than take my word for it: open devtools, select the Network tab, paste a token. Nothing fires. Then close the tab - still no network traffic. No analytics, no cookies, no beacons anywhere on the site. Source is public if you want to read it: github.com/kalisada/tokenbench

Collapse
 
raju_dandigam profile image
Raju Dandigam

The rule of never sending the token to a server is exactly the kind of constraint that changes whether a debugging tool is safe to use under pressure. I also like the split between signature validity and time validity, because a lot of JWT tooling collapses those into a vague green or red result that does not help during incident triage. In practice the next useful step is making the unsafe cases impossible by design, not just documented in a warning banner. Nice example of turning a security footgun into a product constraint.

Collapse
 
mike_0f35516d33902a0d2546 profile image
Mike

"Impossible by design, not documented in a warning" is the phrasing I wish I'd used in the article. Thanks.

Collapse
 
voltagegpu profile image
VoltageGPU

Interesting take on JWT debugging—removing the risk of token exposure is a solid security win. In my work with secure GPU environments, I've seen similar patterns where isolating sensitive operations in air-gapped or confidential computing contexts can eliminate side-channel risks. It's reassuring to see this principle applied to a common developer tool.

Collapse
 
mike_0f35516d33902a0d2546 profile image
Mike

Thanks - same principle, different scale: the cheapest side channel to eliminate is the one you never create.

Collapse
 
alexshev profile image
Alex Shev

Browser-local inspection is the right trust boundary for this kind of tool. The best security UX is not a warning that users will eventually ignore, but a design where the risky data never leaves.

Collapse
 
mike_0f35516d33902a0d2546 profile image
Mike

That's exactly it. A warning is a request to be careful; a design where the data can't leave doesn't need the user to be careful at all.