{{ summaryHeading }} {{ summaryPrimary }} {{ summaryLine }} {{ algorithmBadge }} {{ validityBadge }} Not verified

JWT source inspection
Use one two- or three-part compact token. Five-part encrypted JWE content needs decryption keys and is not decoded here.
{{ sourceStatus || 'Drop a TXT or JWT file onto the textarea; maximum 256 KiB.' }}
Only the first non-empty line was decoded; {{ ignoredLineCount }} extra line{{ ignoredLineCount === 1 ? '' : 's' }} ignored.
Enter a JSON object for a test token. The exact UTF-8 source, including whitespace, is signed. Claims stay readable; signing does not encrypt them.
Operation:
Use a disposable test key of 32–4,096 UTF-8 bytes. Spaces count exactly; no Base64 or hex decoding. It stays in this tab, is excluded from exports and links, and clears when you leave signing mode. Use a secure context (HTTPS or localhost).
JSON object with alg exactly "HS256". Add optional typ or kid values as needed. Conflicting algorithms, crit and b64 extensions are rejected.
Blank skips issuer comparison and leaves the decoded claims unchanged.
Use one value or a comma-separated list. A match to any configured value passes this local comparison.
Zero uses the exact review time. This setting cannot prove what another verifier accepts.
sec
This comparison applies only when both numeric iat and exp claims are present.
hours

{{ signedTokenStatus }}

{{ signedToken }}
SourceNameTypeValueReviewCopy
{{ row.source }}{{ row.name }}{{ row.type }}{{ row.value }}{{ row.review }}

{{ ledgerExportStatus }}

SignalCheckEvidenceNext stepCopy
{{ row.severity }}{{ row.check }}{{ row.evidence }}{{ row.next_step }}

{{ reviewExportStatus }}

{{ chartExportStatus }}

{{ workflowAnnouncement }}

Similar tools

A rejected API request can come from a token meant for another service, an expired session, or a signature the receiver cannot verify. A JSON Web Token (JWT) carries claims that help distinguish these situations. Claims are statements about a subject, such as a user or service, together with details about the issuer, intended recipient, and permitted time window. Reading them is useful when investigating authentication failures or preparing controlled tests.

A signed JWT commonly travels as three sections separated by periods, written as header.payload.signature. The header describes the cryptographic method, the payload contains a JSON object of claims, and the last section contains a signature or authentication code. Base64URL represents their bytes using characters suitable for URLs. The compact appearance can conceal the meaning from a casual glance, but anyone holding an ordinary signed JWT can recover its header and claims.

Decoding
Reverses the encoding so the contents can be read. It needs no signing key and provides no proof of who created the token.
Signing and verification
Signing creates a cryptographic check over the original content. Verification checks that content using an allowed algorithm and a trusted key.
Encryption
Protects the contents from readers without the decryption key. Signing alone leaves claims readable. JSON Web Encryption (JWE) provides a separate encrypted representation.

HS256 uses a hash-based message authentication code (HMAC) with SHA-256. The same secret key is used to create and verify the code. Anyone who knows that key can create another token, so an HS256 check cannot distinguish one key holder from another. There is no verification-only public key for HS256. Key secrecy and unpredictability matter as much as the algorithm name.

Even a correctly verified token needs the receiving application's approval. An issuer claim must correspond to a trusted issuer, the audience must include the intended receiver, and the time claims must satisfy that receiver's policy. A role written into a payload does not grant permission by itself. Revoked sessions, disabled accounts, and application-specific restrictions can also prevent access.

These distinctions explain why two tokens with similar readable claims can behave differently. One may have the wrong recipient, a different signature, or dates interpreted against a different clock. Disposable test tokens help reproduce those cases without using real account data. They should use test keys and invented claims because both the compact token and any decoded copy can disclose the payload.

How to Use This Tool:

The page opens in Decode with demonstration content. Choose that mode to inspect an existing token, or Sign HS256 to create a disposable test token.

Inspect an existing token

  1. Paste a complete token into JWT source. A Bearer prefix or an Authorization: Bearer line is accepted. Alternatively, choose Browse JWT or drop a local JWT or TXT file onto the input. Files may be up to 256 KiB.
    Keep the complete compact token on one line. Only the first non-empty line is decoded; additional non-empty lines are ignored with a warning.
  2. Set optional comparisons under Advanced using the receiving application's trusted configuration. Expected issuer and Expected audience start blank. Clock skew starts at zero seconds, and Maximum TTL review starts at zero, which disables the lifetime comparison.
  3. Read the automatically updated Decoded ledger and Security review, then use Validity timeline to compare the claim dates with the recorded review time.
    For a token-format error, correct the compact value or choose Load sample to restore demonstration content. A policy-input error needs that setting corrected separately; loading the sample does not reset the policy fields.

Create an HS256 test token

  1. Select Sign HS256 and enter a JSON object in Claims payload. Replace the example claims and fixed dates with your intended test data. The default Protected header under Advanced already selects HS256.
  2. Enter a disposable Test signing key of 32 to 4,096 UTF-8 bytes. Leading and trailing spaces count as part of the key. A long key is not automatically an unpredictable key.
  3. Select Sign test token and wait for Signed locally and the Signed test token result. Editing the claims, header, or key removes the previous signed output immediately. If an edit happens during signing, the earlier result is discarded; sign again after completing the changes.
    Correct any named JSON, header, or key error before retrying. A secure-context warning means signing needs browser Web Crypto through HTTPS or localhost. Decoding remains available when signing is unavailable.
  4. Choose Copy token or Download JWT for your controlled test, or choose Inspect token to send the generated token to Decode. Its inspection still shows Not verified.

Interpreting Results:

Not verified applies to every inspection, including a token just created here. Signed locally confirms that the signing operation completed. The decoder does not subsequently verify that signature or establish that another application will accept the token.

  • Decoded ledger shows the compact sections and top-level header and payload members with their JSON types. Arrays and nested objects remain JSON values. The claim count includes only top-level members, not every value nested inside them.
  • Security review gives individual checks an Info, Pass, Review, or Fail signal. A pass belongs only to the comparison named in that row. Read its evidence and next step before drawing a conclusion about issuer, audience, or lifetime.
  • Validity timeline places numeric iat (issued at), nbf (not before), and exp (expiration) claims alongside Review time in UTC. With no numeric time claims, only the review time remains.

The review time is captured when the page opens and refreshed by Load sample. Waiting, editing a token, or signing another token does not advance it. The expiry message is therefore a comparison with that recorded time, not a live countdown. To inspect your token against a fresh review time, retain its text, load the sample, and paste your token again.

Time-active means the ordered time checks found no blocking timing condition under the selected tolerance. No exp means no usable numeric expiration was found after the earlier timing checks; it does not establish indefinite validity. The built-in sample's signature is demonstration data, so its readable claims are not evidence of a genuine issuer signature.

Resolve failed or review signals against the real receiving service's configuration. That service still needs signature verification, trusted issuer and audience checks, and its own authorization and revocation rules. For a debugging record, the ledger and review support row copying and CSV or DOCX reports; the timeline supports image or CSV downloads. Those records describe the inspection, not an authentication decision.

Technical Details:

JSON Web Signature (JWS) compact serialization joins an encoded protected header, encoded payload, and encoded signature with two periods. For an ordinary signed JWT, the first two sections contain UTF-8 JSON objects. Their bytes can be decoded independently of any cryptographic verification.

Transformation core

Base64URL uses letters, digits, -, and _, with no trailing = padding in compact JWTs. Decoding recovers bytes first; only the header and payload are then interpreted as text and JSON.

How the three compact JWT sections are decoded
Section Decoded material Meaning
Header UTF-8 JSON object alg names the algorithm, typ identifies the token type, and kid can identify a key. These are declarations, not trusted algorithm or key configuration.
Payload UTF-8 JSON object Claims such as iss (issuer), sub (subject), and aud (audience) become readable with their types. No decryption is involved.
Signature Arbitrary bytes The byte count can be inspected even when the bytes are not valid UTF-8. Their presence does not establish a correct signature.

Each non-empty section must use the unpadded Base64URL alphabet. A section whose length leaves a remainder of one when divided by four cannot be decoded. Invalid UTF-8, malformed JSON, and a header or payload that is an array, scalar, or null cause decoding to fail. Spaces inside the compact sections and ordinary Base64 characters such as +, /, or = are not accepted.

Two-part header.payload input and three-part input with an empty final section are accepted for inspection. They receive a missing-signature signal. A two-part value is an inspection convenience rather than a complete signed JWS. Five-part compact JWE content is rejected because its claims require decryption; other part counts are also rejected. Decoding does not enforce an algorithm-specific signature length, so even an HS256 label beside 32 signature bytes remains unverified.

Pasted input is limited to 262,144 UTF-16 code units after outer whitespace is removed, including extra lines before they are discarded. For ordinary ASCII token text, each character counts once. Local files have a separate 256 KiB limit of 262,144 bytes. Parsed header and claim members are displayed alphabetically; the ledger is not a byte-for-byte reproduction of the original JSON.

HS256 signing and exact bytes

HS256 computes a 32-byte HMAC using SHA-256 and a shared secret. The authenticated message is the two encoded JSON sections joined by one ASCII period. The exact text matters at every stage.

  1. Convert the original protected-header text to UTF-8 bytes and encode those bytes as unpadded Base64URL.
  2. Convert the original claims-payload text to UTF-8 bytes and encode it the same way.
  3. Join the two encoded strings with . and compute HMAC-SHA-256 over the resulting ASCII bytes, using the exact UTF-8 bytes of the entered key.
  4. Encode the 32-byte HMAC as 43 unpadded Base64URL characters and append it after a second period.

Valid JSON whitespace, property order, line endings, and escape spellings are preserved in the signed text. Reformatting a JSON object can therefore change its token and MAC while leaving its decoded claims unchanged. Identical header, payload, and key bytes produce the same HS256 token. No issue time, expiration, identifier, or other claim is inserted or refreshed automatically.

The signing checks are deliberately narrower than the range of tokens that can be inspected:

  • The protected header must be a JSON object with alg exactly equal to the string HS256. Missing, differently cased, or alternative algorithms are rejected. The default header is {"alg":"HS256","typ":"JWT"}.
  • Top-level protected-header names must be unique after JSON escape decoding. For example, alg and \u0061lg name the same member. Any presence of crit or b64 is rejected, including an empty array or a value that appears harmless. These extensions are not supported for signing.
  • The claims payload must also be a JSON object. Both JSON texts, the member names and string values retained after parsing, and the key must contain well-formed Unicode. Unpaired surrogates in these are rejected. These checks do not validate the claims against an application's required schema or permissions.
  • The key is literal UTF-8 text, with an inclusive size range of 32 to 4,096 bytes and at least one non-whitespace character. Spaces are retained, and Unicode is not normalized. Base64, hexadecimal, JSON Web Key (JWK), and private-key formats are not imported or decoded. A hexadecimal-looking string is still used as its literal text bytes.
  • Each JSON text is limited to 262,144 UTF-16 code units. The complete generated token must also fit within 262,144 ASCII characters, including both periods and the encoded MAC. UTF-8 encoding and Base64URL expansion can make the final limit stricter than the individual text limits.

The 32-byte minimum matches HS256's minimum key size, but length does not measure entropy, or unpredictability. Repeated characters and memorable passwords can satisfy a size check while remaining guessable. The signing operation does not derive or strengthen a password. Duplicate-name rejection applies specifically to the signing header; repeated payload names are not specially rejected, and ordinary JSON parsing displays the last occurrence. Unique claim names avoid that ambiguity.

A reproducible synthetic signature

This example uses disposable synthetic test data and a deliberately public key. It is not a production access token. Enter the following strings exactly, with no added whitespace or trailing newline:

Protected header
{"alg":"HS256","typ":"JWT"}
Claims payload
{"sub":"demo-user","role":"reader"}
Disposable text key
0123456789abcdef0123456789abcdef

The header contains 27 UTF-8 bytes, the payload 35 bytes, and the key 32 bytes. Encoding the first two produces an 84-byte ASCII signing input when joined with one period. The intermediate values are:

Base64URL values for the disposable HS256 example
Stage Exact value
Encoded header eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Encoded payload eyJzdWIiOiJkZW1vLXVzZXIiLCJyb2xlIjoicmVhZGVyIn0
Encoded HMAC 8shRFbDzOTwzSeLdm_EmwcSP67ufSaSUMhYnpix1nAM

The complete token contains 128 ASCII characters:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkZW1vLXVzZXIiLCJyb2xlIjoicmVhZGVyIn0.8shRFbDzOTwzSeLdm_EmwcSP67ufSaSUMhYnpix1nAM

Inspection shows two payload claims, 32 signature bytes, No exp, and Not verified. No expiration was added during signing. Adding one space after the comma in the payload changes the MAC even though the subject and role still read the same.

Formula core: time and lifetime

JWT time claims use NumericDate, a number of seconds since 1 January 1970 at 00:00:00 UTC, ignoring leap seconds. Here, only finite JSON numbers qualify. Quoted numbers are not converted, and millisecond timestamps are not automatically corrected. Fractional and negative numeric values can be read, so numeric acceptance alone is not a plausibility check.

Let t be the recorded review time in seconds, s the clock-skew allowance in seconds, and H the maximum lifetime in hours. Remaining time compares expiration with the review time; token lifetime spans issue time to expiration. The policy limit is converted to seconds for comparison.

Remaining seconds=exp−t Lifetime seconds=exp−iat Limit seconds=3600×H

The time label uses the first matching rule below. Later conditions do not replace an earlier result, so the summary is not a simultaneous report of every possible timing problem.

JWT time classification in evaluation order
Order Time label Condition
1 Expired Numeric exp exists and t−s≥exp.
2 Not active yet Otherwise, numeric nbf exists and t+s<nbf.
3 Future iat Otherwise, numeric iat exists and t+s<iat.
4 Time-active No earlier rule matched and numeric exp exists.
5 No exp No earlier rule matched and no finite numeric exp exists.

At zero skew, equality with exp is expired, while equality with nbf or iat does not trigger a future-time warning. The future-issued warning is a local diagnostic; RFC 7519 defines iat as issue time without making this particular rejection rule universal. Clock skew accepts whole seconds from 0 to 86,400. That input range is not a recommendation to allow a day of tolerance.

Skew changes the timing decision without moving any claim dates or changing the raw remaining-time calculation. With no other future-time condition, an expiration 59 seconds before the recorded review time is still Time-active with 60 seconds of skew. At 60 seconds after expiration it becomes Expired. A negative remaining-time message can therefore appear inside the tolerated window.

Maximum lifetime accepts 0 to 87,600 hours. Its comparison runs only when the limit is positive and both iat and exp are finite numbers. A negative lifetime produces Fail, a lifetime strictly greater than the limit produces Review, and a nonnegative lifetime at or below the limit produces Pass. Equality passes. With the default zero limit, this check is disabled, including its reversed-date check.

Comparisons retain fractional seconds, while displayed UTC timestamps round down to a whole second. Relative durations use whole seconds below one minute, then minutes below one hour, hours below one day, and days thereafter. Minutes, hours, and days show one decimal below ten of the selected unit and whole values from ten upward. These display choices do not change the timing boundaries.

Identity and security signals

Issuer and audience comparisons use the configured expectations, not a discovered trust relationship. The configured text is trimmed, matching is case-sensitive, and a comma-separated audience list passes if any configured value matches any decoded audience value. A single audience or an audience array can be inspected. These comparisons convert decoded values to text; they are not strict claim-type or schema validation. Blank expectations skip their comparisons. Policy settings do not rewrite claims or change a generated token.

During decoding, a missing alg produces Review and none produces Fail. Other declared algorithms are reported as information, without establishing whether they are supported or acceptable to the receiving service. Missing signature bytes produce Fail; present bytes still produce Review because no cryptographic verification occurs.

A non-empty crit array prompts review, but its extension requirements are not evaluated. Names such as password, client_secret, and api_key can trigger the readable-sensitive-claims warning. This scans top-level claim names only, not nested fields or the contents of values. An absence of that warning does not establish that a payload contains no secrets.

Privacy Notes:

Token decoding, policy comparisons, and HS256 signing process your data locally in the browser. Token text, signing JSON, and the signing key are not uploaded for processing or saved to browser storage by this tool. Copying or downloading a token or report creates a separate copy of its readable content.

  • Only Expected issuer, Expected audience, Clock skew, and Maximum TTL review are reflected in the page URL and restored from it. Shared links and browser history can therefore contain these policy values, but not the entered token, signing JSON, or key.
  • The signing key is excluded from generated tokens, reports, links, and requests. Copied or downloaded tokens and reports can still contain readable claims. If you put a secret directly into the payload or header, it becomes part of that readable content.
  • Clear key, leaving signing mode, and leaving the page clear the current key. Inspect token leaves signing mode and clears the key while retaining the transferred token for decoding. Merely switching to another browser tab does not perform that clearing.
  • Clearing the key cannot recall clipboard contents, downloaded files, or a token already transferred to the decoder. JavaScript memory management also prevents a promise of secure erasure, so use disposable test keys and synthetic claims.