Verify signature (HMAC)
For your own tokens — paste the HMAC secret and check whether the signature is valid. Runs entirely in your browser using the Web Crypto API; the secret never leaves your machine.
What a JWT actually is
A JSON Web Token (RFC 7519) is three base64url-encoded strings joined by dots: header.payload.signature. The header declares the signing algorithm; the payload contains claims (small JSON facts about the token's owner and validity); the signature proves the token wasn't tampered with. The encoding is base64url, not base64 — different alphabet (- and _ instead of + and /), no padding.
Crucially, the payload is not encrypted. Anyone with the token can decode it and read the claims — this tool does exactly that. The signature only proves authenticity. If you put sensitive data in a JWT payload, anyone who intercepts the token sees it. For genuinely confidential information, use JWE (JSON Web Encryption) instead, or keep the data server-side and reference it by an opaque token ID.
Standard claims you should recognize
iss(issuer) — who created the token. Often a URL likehttps://auth.example.com.sub(subject) — who the token is about. Usually a user ID.aud(audience) — who the token is for. A specific service or client ID.exp(expiration) — Unix timestamp in seconds (not milliseconds). After this time, the token must be rejected.nbf(not before) — Unix timestamp before which the token is not valid.iat(issued at) — when the token was created.jti(JWT ID) — a unique identifier, useful for token revocation lists.
Beyond these, you'll see custom claims like email, name, roles, scope, tenant_id. These are application-specific — there's no registry for them. Convention is to use short names for size, since JWTs travel in HTTP headers where size matters.
The algorithms — and the trap
The alg field in the header tells the verifier which algorithm to use. Common values: HS256 (HMAC + SHA-256, symmetric — same secret signs and verifies), RS256 (RSA + SHA-256, asymmetric — private key signs, public key verifies), ES256 (ECDSA + SHA-256, asymmetric and shorter).
The "none" algorithm trap. Early in JWT's history a vulnerability appeared where "alg": "none" would cause some libraries to skip signature verification entirely. An attacker could forge any token by setting alg=none and providing an empty signature. Modern libraries reject "none" by default. If you're writing JWT verification by hand, always explicitly check that alg is one of an allowlist — never trust what's in the token header to select the algorithm.
The "algorithm confusion" trap. Even nastier: if your code accepts both HS256 and RS256, and uses the public key as the HMAC secret when alg=HS256, an attacker can sign any payload using your published public key as the secret. Always pin the expected algorithm per endpoint; don't infer it from the token.
Verification this tool can and can't do
This tool verifies HMAC algorithms (HS256, HS384, HS512) using the Web Crypto API in your browser. You paste the secret, the tool computes the HMAC of the header+payload and compares with the signature. For HMAC, the secret is the same on the signing side and the verifying side — so you must have access to it to verify.
What it doesn't do: RS256, ES256, PS256 verification require the issuer's public key (typically fetched from a jwks_uri endpoint). The Web Crypto API can do RSA verification, but the workflow is more complex — paste the public key, parse PEM, etc. If your use case needs RSA verification regularly, a dedicated tool like jwt.io's verifier or a CLI like step crypto jwt verify is the right answer. This tool is focused on the common case: "I have an HMAC-signed JWT and want to confirm the signature is valid against my secret."
Common pitfalls when implementing JWT auth
- Storing JWTs in localStorage. Convenient but vulnerable to XSS — any script on your page can read them. The defensible alternative is an HttpOnly cookie with SameSite=Lax or Strict, scoped tightly to your API origin.
- Treating JWTs as session tokens. JWTs are bearer tokens — anyone who has the token can use it. There's no built-in way to revoke a JWT before it expires. If your security model requires immediate revocation (logout, password change, account compromise), you need a server-side revocation list, which partly defeats the "stateless" benefit of JWTs.
- Long expiration times. If you can't revoke them, keep them short — 5–15 minutes is typical. Pair with refresh tokens stored more carefully.
- Putting too much data in the payload. Every request now carries this in a header. A 4KB JWT × every request adds up; cookies have a 4KB total limit; some proxies refuse oversized headers. Keep claims minimal; look up other data server-side by user ID.
- Trusting the token without re-checking claims. Valid signature ≠ valid request. Always also check
exp,iss, andaud. Some libraries do this for you; some don't.
Common use cases
- Inspect a JWT to debug auth failures
- Verify a token your service signed using the shared HMAC secret
- Check whether a token is expired
- Examine claims structure when integrating with an OAuth/OIDC provider
Frequently asked questions
Is my JWT or secret sent to a server?
No. The decoder and HMAC verifier both run entirely in your browser using the Web Crypto API. Open DevTools → Network tab and watch — there are no requests when you decode or verify.
Why does the tool only verify HMAC, not RSA?
HMAC (HS256/384/512) is the common case where you have access to the shared secret. RSA (RS256) and ECDSA (ES256) verification requires the issuer's public key (typically from a JWKS endpoint). For RSA verification, dedicated tools or your auth provider's library are more reliable. We may add RSA support if there's demand.
What's the "none algorithm" trap?
A historical JWT vulnerability where setting <code>alg: "none"</code> caused some libraries to skip signature verification entirely. Modern libraries reject "none" by default. When writing JWT verification by hand, always check that <code>alg</code> is in an allowlist — never trust the token's own header to pick the algorithm.
Are JWTs encrypted?
No — only signed. Anyone with the token can decode and read the payload. If you need confidentiality, use JWE (JSON Web Encryption) instead, or store sensitive data server-side and reference it by an opaque token ID.