🔐 JWT Decoder & Verifier

Last updated: June 2, 2026

🔐 JWT Decoder & Verifier

Decode headers, payloads, inspect claims & expiry, verify HS256/RS256 signatures — 100% in-browser, zero data sent anywhere.


Enter the HMAC secret. Toggle to show/hide: Show
Header
Payload
Claims Inspector
Signature Verification
Not verified (no secret/key provided)

Understanding JWT Tokens: Decoding, Claims, and Signature Verification Explained

JSON Web Tokens have become the backbone of modern authentication. Whether you are building a REST API that issues access tokens, integrating with a third-party OAuth provider, or debugging why a user keeps getting logged out unexpectedly, the ability to decode and verify a JWT on-demand is something every developer needs in their toolkit. The problem is that most JWT tools either require you to paste tokens into a server-side form — which carries real privacy risks — or demand that you install a package just to read three base64url-encoded strings. Neither is acceptable when you are in the middle of a debug session.

This tool handles everything in the browser using the native Web Cryptography API. No data leaves your machine. The JavaScript executing the verification is the same JS engine your browser uses for everything else — it is auditable, it is fast, and it works offline once the page is loaded.

Anatomy of a JWT

A JWT is three base64url-encoded chunks joined by dots: header.payload.signature. The header tells you the signing algorithm and token type. The payload carries the claims — structured assertions about the subject, like their user ID, roles, token lifetime, and issuer. The signature is a cryptographic proof that the first two parts were not tampered with after the token was issued.

Base64url encoding is not encryption. Anyone who possesses a JWT can decode the header and payload in seconds without knowing the secret. This is by design: JWTs are meant to be read by resource servers that need to inspect claims before granting access. The secret only protects the integrity of the data, not its confidentiality. If you need to protect the payload contents, you need a JWE (JSON Web Encryption) token — a different standard entirely.

Standard Claims You Will Always Encounter

The JWT specification (RFC 7519) defines a set of registered claim names. Understanding what each one means is the foundation of debugging authentication problems:

sub (Subject) — the principal that is the subject of the JWT. In practice this is almost always the user ID from your database.

iss (Issuer) — identifies the entity that issued the token. Your backend will typically validate that this matches the expected issuer before trusting any other claims.

aud (Audience) — identifies the recipients for which the token is intended. A token issued for your mobile app should not be accepted by your admin dashboard, and validating the aud claim is how you enforce that boundary.

exp (Expiration Time) — a Unix timestamp after which the token must not be accepted. This is the single most common source of "why is my user getting logged out" bugs. The claim is in seconds since the Unix epoch, and off-by-one issues from confusing milliseconds with seconds are embarrassingly common.

iat (Issued At) — when the token was created. Useful for measuring token age and for implementing token revocation by rejecting tokens issued before a certain time (e.g., after a password reset).

nbf (Not Before) — the token must not be accepted before this time. Less commonly used than exp, but essential if you need to issue tokens in advance that only become valid at a scheduled future moment.

jti (JWT ID) — a unique identifier for the token. Required if you are maintaining a token blocklist for logout or revocation, since you need something to key the blocklist entries against.

HS256 vs RS256: Choosing the Right Algorithm

The algorithm declared in the header determines how the signature is computed and verified. The two algorithms you will encounter most often are HS256 and RS256, and they represent fundamentally different trust models.

HS256 (HMAC-SHA256) uses a shared secret. The same secret is used to sign the token and to verify it. This means every service that needs to verify JWTs must have access to the secret. It is simple, fast, and appropriate for single-service architectures or when all your services are under a single trust boundary. The critical security requirement: the secret must be a cryptographically random value of sufficient entropy. Using a human-readable password or a short string as an HS256 secret is a well-known vulnerability — attackers can brute-force short secrets offline using only the token, no network access required.

RS256 (RSA-SHA256) uses an asymmetric key pair. The private key signs the token and must be kept secret on the issuing server. The public key verifies the token and can be distributed freely to any service that needs to validate tokens. This is the right choice for distributed systems, microservices architectures, and public APIs. It allows you to publish your public keys at a JWKS (JSON Web Key Set) endpoint and let third-party services verify your tokens without ever exposing your private key.

The algorithm-confusion attack is worth knowing about: a malicious actor can modify the JWT header to change "alg":"RS256" to "alg":"HS256" and then sign the token using the public key as the HMAC secret. Vulnerable libraries that use the algorithm from the token header (rather than enforcing the expected algorithm server-side) will incorrectly validate the forged token. Always validate the algorithm on the server, never trust the alg claim blindly.

The "alg: none" Vulnerability

The JWT specification technically allows tokens with no signature by setting "alg":"none" and omitting the signature portion. Some early library implementations accepted these tokens without any verification whatsoever, allowing attackers to forge arbitrary claims. No production system should accept alg: none tokens. This tool correctly surfaces the algorithm in the header so you can immediately spot a token with this dangerous configuration.

Common JWT Debugging Scenarios

When an API returns a 401 Unauthorized response and you have the token in hand, the first three things to check are: Is the token expired (check exp against current time)? Is the token being sent to the correct audience (check aud)? And is the issuer what the server expects (check iss)? Expiry issues account for the majority of "my auth is broken" reports in production. Clock skew between servers — even a few seconds — can cause tokens to appear expired to one service but valid to another.

The second most common issue is testing with a token signed against a development secret while the production secret is different. Decoding the payload tells you nothing about whether the signature is valid — you have to actually run the HMAC or RSA verification with the correct key. That is exactly what this tool does for you locally, so you can rule out key mismatch without writing a test script or firing up Postman.

Security Note on In-Browser Verification

This tool is designed for development and debugging. It uses the Web Cryptography API (window.crypto.subtle), the same API that powers HTTPS in your browser and is available in every modern browser without polyfills. The cryptographic operations are implemented by the browser's native crypto engine — not JavaScript — so they are both correct and performant. The important thing: your JWT and your secret never touch any external server. This makes it safe to use with real tokens during a debug session, though you should still rotate any secret that was used in a shared environment immediately after debugging is complete.

FAQ

Is it safe to paste a real JWT token into this tool?
Yes. All decoding and verification happens entirely in your browser using the native Web Crypto API. No data is sent to any server. The tool works offline once loaded. That said, treat any secret or private key you type here like a password — avoid using it on shared or public computers.
Why can I read the JWT payload without a secret key?
JWT payloads are base64url-encoded, not encrypted. Any party holding the token can decode the header and payload without knowing the secret. The signature only proves that the token was not tampered with after issuance — it does not protect the payload from being read. If you need to keep payload data confidential, use JSON Web Encryption (JWE) instead.
What is the difference between HS256 and RS256?
HS256 uses a shared symmetric secret — the same key signs and verifies the token, so every service that validates tokens must have the secret. RS256 uses an asymmetric RSA key pair — the private key signs the token on the issuer, and the public key (which can be freely distributed) verifies it. RS256 is preferred for distributed systems and public APIs because resource servers never need access to the signing private key.
My token shows as expired. How do I fix it?
An expired token means the exp (expiration) Unix timestamp has passed. The fix is to obtain a new token from the issuing server — usually by calling the refresh token endpoint or re-authenticating. If tokens are expiring too quickly in development, check the token lifetime setting on your auth server. Also verify that the clocks on your client and server are synchronized, since clock skew can cause tokens to appear expired prematurely.
Why does RS256 verification fail even though my public key looks correct?
The most common cause is key format. This tool expects the public key in SPKI PEM format (the block that starts with -----BEGIN PUBLIC KEY-----). If you have a certificate (-----BEGIN CERTIFICATE-----) or a PKCS#1 key (-----BEGIN RSA PUBLIC KEY-----), you will need to convert it first using openssl: openssl x509 -pubkey -noout -in cert.pem for a certificate, or openssl rsa -pubout -in key.pem for a PKCS#1 key.
What is the alg:none attack and should I worry about it?
Some early JWT libraries accepted tokens with alg set to 'none' and no signature, trusting the payload without any cryptographic verification. Attackers could forge arbitrary claims this way. Modern, well-maintained libraries reject alg:none by default. You should still explicitly whitelist the algorithm(s) your application accepts on the server side rather than trusting the alg value in the incoming token header.