JWT Inspect

Decodes a JWT on the page, without sending it anywhere, and flags alg none, key confusion, expiry and readable secrets.

StopsHelps keep live tokens out of online decoders

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/jwt-inspect.json

The files land in your project. They're yours to edit.

Usage

jwt-inspect-usage.tsx
import { JwtInspect } from "@/components/ui/jwt-inspect"

<JwtInspect value={value} onValueChange={setValue} algorithms={["RS256"]} audience="api.acme.dev" issuer="https://auth.acme.dev" now={NOW} timeZone="Asia/Kolkata" />

What it catches

  • alg none, and algorithms outside your allow-list
  • HS/RS confusion (HMAC where an RSA key is expected)
  • Expired token or nbf in the future, shown in local time
  • Timestamps in milliseconds instead of seconds
  • Missing or wrong aud/iss, very long lifetime
  • Secrets or personal data readable in the payload

Props

JwtInspect props
PropTypeDefault
value

Controlled token text.

string—
defaultValue

Initial text when uncontrolled.

string""
onValueChange

Called with the new text on every edit or clear.

(value: string) => void—
algorithms

Algorithms your verifier allows, e.g. ["RS256"]. Others are flagged, and HS* against asymmetric as key confusion.

string[]—
audience

The audience this token should carry.

string—
issuer

The issuer this token should come from.

string—
type

The typ the header must carry, e.g. "at+jwt" (RFC 8725 explicit typing). Off by default.

string—
timeZone

IANA time zone for readable times. Defaults to the user's own.

string—
locale

Locale for readable times. Defaults to the user's own.

string—
now

Fixed "now" (for tests and demos). Otherwise the clock, ticking quietly.

Date | number—
leeway

Clock skew allowed, in seconds. Default 60.

number60
maxLifetime

Lifetimes longer than this many seconds are flagged. Default one day.

number86_400
label

Visible label; shorthand for `messages.label`.

string—
messages

Every visible string. `{detail}`, `{claim}`, `{span}`, `{time}`, `{allowed}`, `{audience}`, `{issuer}`, `{type}` are filled in.

Partial<JwtInspectMessages>—

* required. Also accepts the native props of <textarea> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Visible label; input has aria-invalid and is described by the local-only note and the report
  • Report uses role=status; icons are aria-hidden
  • Clear button has an aria-label and returns focus to the field

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Verify the signature, algorithm, issuer, audience and expiry on the server with a JWT library and a pinned key.

  • Decodes only; the signature is never verified
  • Secret and PII detection is pattern-based
  • Doesn't make a token safe to use; your server must verify it

Rules

Rules last verified:

Informational. Checked RFC 8725's guidance on alg none and algorithm substitution; individual thresholds (lifetime, leeway) are product defaults, not standards. Copied components don't update themselves, so re-check after rule changes (see updating a component).

What it stops

Helps keep live tokens out of online decoders

Also known as

jwt · jwt decoder · jwt.io · bearer token · access token · id token · claims · exp

Dependencies

Pairs well with