Developer Tools

Deep JWT Inspection Without Transmission: Client-Side Token Analysis with CapyToolkit

18 min read
Inspect JWTs without transmitting tokens

You pull a JWT from localStorage after a successful login. It’s granting access to a production API, one that holds sensitive data. You need to inspect the claims, check the expiry, and verify the audience. The catch? You can’t upload this token to a random website. Many online JWT inspection tools require sending the token to the site running the decoder. For production credentials, that’s unacceptable: anyone who captures your token can impersonate you until it expires.1

Consider the workflow for a healthcare API. The token contains a patient_id claim and a scopes array granting access to /medical-records. Uploading this token to a third-party site transmits both identifiers and scopes. Even if the site itself is honest, a single exposed logging endpoint or CDN misconfiguration could leak the token. The stakes aren’t hypothetical; real breaches have occurred from less.

CapyToolkit’s JWT decoder that runs entirely in your browser without sending tokens to any server solves this problem at the source. The tool uses native APIs like atob() and JSON.parse. Once the page loads, it works offline. There are no network requests, no uploads, and no telemetry. The tool doesn’t know, or care, what tokens you paste. Your production credentials never leave the tab.

The security model is strict: zero-trust at the tool level. The tool doesn’t track how often you use it, what tokens you decode, or even whether you paste anything at all. There’s no analytics script, no error reporting, and no external dependencies. The only network request is the initial page load; and you can disconnect immediately after.

This isn’t just a convenience; it’s a security win. Zero-cloud engineering means eliminating attack surfaces that don’t need to exist. When your tokens stay local, there’s nothing to intercept and nothing to exploit. For teams handling regulated data, offline inspection reduces third-party exposure; it is a practical baseline for sensitive workflows. Offline inspection isn’t a luxury; it’s a baseline requirement.

Decoding the Three-Part Structure

JWTs are short strings with three parts, separated by dots: header.payload.signature. At first glance, they look like random characters. In reality, each part uses Base64Url encoding, a URL-safe variant that removes padding characters from standard Base64, to represent a JSON object or cryptographic signature as standardized in RFC 7519, which defines the registered JWT claim names.2 The encoding isn’t encryption; it’s simply a way to pack structured data into a URL-safe string. Anyone with a JWT can decode it, but not everyone can verify its authenticity.3

The format exists for a reason: statelessness. APIs don’t need to store session data; the token carries everything the server checks during each request.3 When you pull a JWT from an Authorization: Bearer *** header, you’re holding a miniature credential passport. The server signs it, then forgets it until the next request arrives. This simplicity comes with a responsibility: you must read and understand what’s inside.

Most JWTs use UTF-8 throughout. The header specifies the signing algorithm (e.g., alg: HS256) and token type (almost always typ: JWT). The payload contains the claims: iss (issuer), sub (subject), aud (audience), iat (issued-at), and exp (expiration). Custom claims follow the same structure. A production token for a SaaS platform might include org_id, user_roles, or feature_flags.

The signature is the hardest part to understand. It’s not JSON; it’s a cryptographic hash of the header and payload, signed with a key. For HS256, a shared secret signs and verifies. For RS256, the issuer’s private key signs, and your server’s public key verifies. The signature proves the token hasn’t been tampered with, but only if you verify it.4 Without the correct key, the signature is meaningless.

Header: Algorithm and Token Type

The header is short, usually under 50 characters. Here’s a real-world example: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9. Decoded, it becomes:

{
  "alg": "RS256",
  "typ": "JWT"
}

The alg tells you what key material the signature expects. HS256 uses a shared secret, while RS256 and ES256 use key pairs. The distinction matters for debugging. If your server expects RS256 but receives an HS256 token, verification fails at algorithm mismatch. Client-side decoding reveals this immediately; inspecting the header tells you whether you’re configured for the right cryptographic scheme.

typ is usually "JWT"; this tells parsers what format to expect. Occasionally, you’ll encounter "JOSE" or "JWS". These indicate minor variations in structure, but all share the dot-separated format. For encrypted tokens (JWE), the header carries additional fields like enc for the encryption algorithm and zip for compression.5

Payload: Claims and Custom Fields

The payload is where the action happens. A production token issuer includes iss, sub, aud, exp, and any application-specific fields. Here’s a realistic example:

{
  "iss": "https://auth.example.com",
  "sub": "user-80f9f8bc-fff4-40ed-8e1f-708b1c218ee3",
  "aud": "https://api.example.com",
  "iat": 1710771200,
  "exp": 1710857600,
  "org_id": "health-one",
  "scope": ["read:patient", "write:notes"]
}

Standard claims handle the basics. iss identifies the issuer, the authorization server that created the token. sub is the user or service account ID. aud tells you who the token is meant for; sending it to a different API usually causes rejection. iat and exp establish time windows. Without exp, validators that rely only on that claim have no built-in expiration boundary, a security anti-pattern.3

Custom claims carry application logic. org_id might map to a multi-tenant system, while scope restricts what the bearer can access. Claims like device_id or ip are occasionally added for session binding. Client-side decoding lets you review these instantly. If your app expects scope to include payment:write but the token lacks it, you catch the missing permission before making the API call.

Signature: Why We Don’t Verify Locally

The signature exists to prevent tampering. Servers cryptographically bind your header and payload with a key, creating a hash that changes if you alter either part. For HMAC (HS256), the same key signs and verifies. For public-key algorithms (RS256, ES256), the signing key stays private while the public key verifies. This separation allows distributed verification; any party holding the public key can confirm the token’s integrity without exposing the private key.

CapyToolkit’s decoder shows the algorithm and reveals the Base64Url-encoded signature, but doesn’t validate it. Signature verification requires the original key material, or access to a JWKS endpoint, which pulls public keys from the issuer. Without this, decoding reveals what’s encoded, not whether claims are trustworthy. This design choice is deliberate: it prevents false confidence. Production systems must authenticate server-side using libraries like jose or framework middleware.6 Client-side inspection is the first step; server-side verification is mandatory.

Diagram of a JWT broken into its three dot-separated parts: the `Base64Url`-encoded header showing algorithm and type, the payload showing standard and custom claims, and the cryptographic signature with a note that client-side decoding cannot verify it
Decoding reveals what is in the header and payload, but without the original signing key the signature block is opaque. Verification must happen server-side using the issuer's JWKS endpoint.

Practical Workflow: From Copy to Analysis

You’re debugging an expired token error after an OAuth flow. Instead of searching for “JWT decoder online,” you use CapyToolkit’s JWT Decoder & Claims Inspector; a single static page with no network overhead. Here’s how the workflow unfolds once you paste the token.

The tool triggers decoding instantly. There’s no button to click; paste, and the header, payload, and expiry status appear. Each section is color-coded: blue for header, green for payload, dark for signature. You inspect the algorithm (RS256), check the issuer (auth.your-domain.com), and notice the expiry (exp: 1710857600). The expiry panel confirms what you suspected: “Expired 2 hours ago.” The human-readable delta saves mental math.

After inspection, you copy the payload iss claim. It’s http://localhost:3001; the wrong issuer for production. The authorization server configuration cached a local value and issued tokens with it. This mismatch causes downstream API failures. Client-side decoding surfaces the misconfiguration before escalating to server logs.

The tool handles edge cases cleanly. Missing exp? It displays “No exp claim ; token does not expire.” Malformed Base64? It says “Invalid JWT format.” Missing dots? It flags which part is absent. These sanity checks keep you from chasing ghost bugs.

Step 1: Obtaining a JWT Token Securely

Tokens live in predictable places inside browsers. Open dev tools (F12), switch to the Application tab, and check localStorage and cookies. JWTs in OAuth flows often use access_token or id_token keys. For SPAs, you’ll also find them in Authorization headers within the Network tab; filter by Bearer and click successful API responses.

Once you locate the token, right-click and “Copy value.” Avoid copying the entire header; for example, skip Authorization: Bearer . Tools like Burp Suite or Postman automatically strip headers, but browser dev tools include them.

Security note: clipboards aren’t secure storage. In shared environments or questionable infrastructure, clear your clipboard immediately after decoding. Tools like KeePassXC offer secure clipboard clearing after short delays.

Step 2: Pasting and Automatic Decoding

The tool decodes on paste; no enter key needed. This frictionless design prevents accidental form submissions that trigger unintended network requests. When you paste, the header and payload expand instantly. Each JSON field gains subtle indentation and monospace formatting for readability. You scan immediately for alg (RS256 or HS256) and exp.

Empty fields reveal common mistakes. If the payload shows {} or lacks an iss claim, the token is probably a placeholder from local development. Missing a section entirely? That usually indicates a malformed token missing a separator dot. CapyToolkit detects this and displays a clear error without attempting to decode garbage.

Individual sections support independent copying. Click “Copy” under the payload, and only the decoded JSON transfers to clipboard; not the header or signature. This lets you extract specific claims to cross-check against API documentation.

Step 3: Reading Expiry and Validity Status

The expiry panel sits beneath the payload, synthesizing the exp claim. It converts Unix timestamps (e.g., 1710857600) into human-readable deltas: “Valid for another 2 hours” or “Expired 3 days ago.” This matters when debugging session timeouts; expirations often appear as “401 Unauthorized” responses, not explicit timeout messages.

The panel accounts for clock skew. If your laptop’s clock is 5 minutes fast, CapyToolkit shows the adjusted expiry. Long-lived tokens without exp display a warning; these have no built-in expiration boundary, creating persistent credentials that can’t be revoked without rotating keys. Short-lived exp values reduce the impact if a token leaks.7

When exp is absent, the tool says so explicitly. This prevents false positives during debugging; a missing exp claim enforces no expiration, whereas an expired token must be replaced.

Step 4: Cross-Checking Claims Against Documentation

Claims must align with your API’s expectations. When integrating a third-party OAuth provider, you compare the decoded aud against your client ID. A mismatch means the token targets a different application; common when switching between development and production environments.

For custom claims like roles, you verify that admin maps to your application’s authorization rules. Documentation often sketches expected claims in tables; API guides list scope: ["read:accounts", "write:transfers"]. Cross-checking these saves you from implementing complex permissions only to discover the token lacks them.

iss validation ensures tokens originate from trusted issuers. If your app expects https://auth/yourservice but sees a local IP instead, revoke the token; it’s likely a credential leaked from a development server. These cross-checks happen before implementation, letting you fail fast when claims are misconfigured.

JWT Claims and Algorithms Reference

Decoding is only useful if you understand what each claim means. Below is a quick-reference guide covering standard claims, their semantics, and common values. Registered claims follow the RFC, but implementations vary; always check issuer documentation.

ClaimMeaningExample Value
iss (Issuer)Entity that issued the tokenhttps://auth.example.com
sub (Subject)Principal that is the subject of the token”user-12345”
aud (Audience)Who the token is intended for”api.example.com”
exp (Expiration)Unix timestamp after which the token is invalid; RFC 7519 defines exp and all registered JWT claim names31735689600
nbf (Not Before)Unix timestamp before which token is invalid1704067200
iat (Issued At)Unix timestamp when token was issued1704067100
jti (JWT ID)Unique identifier for the token”a1b2c3d4-e5f6-7890-abcd-ef1234567890”

The exp claim is non-negotiable. Without it, you’re handling persistent credentials that attackers can harvest indefinitely. Similarly, aud ensures tokens target APIs intended for them. A token issued for mobile-app.client should not grant access to internal.admin-api.

Algorithms dictate security posture. HMAC (HS256) is symmetrical; keep the secret key private. RSA (RS256) and ECDSA (ES256) use key pairs, separating signing and verification. Never accept tokens with alg: none, which RFC 8725’s JWT security best practices explicitly require rejecting because this unsigned variant exposes tampering.6

AlgorithmTypeKey RequirementSecurity Notes
HS256HMACShared secretFast, but secret must stay confidential; same key signs and verifies
RS256RSAKey pairIndustry standard; scales better for distributed systems
ES256ECDSAElliptic curveSuitable where your issuer documents it
noneNoneNo signatureDisabled in secure deployments; user agents must reject

Client-side decoding reveals alg, but servers must enforce it during verification. Always configure libraries to reject untrusted algorithms; libraries like jose often allowlist trusted values.

When to Use Client-Side Decoding

Client-side decoding shines during local development. You’re integrating an OAuth provider’s metadata endpoint. The metadata document can publish issuer and jwks_uri.8 Before trusting it, inspect sample tokens in-browser. Client-side decoding reveals iss and alg, letting you cross-check these against the metadata document. The Auth0 JWT format viewer for checking issuer claims and algorithms without uploading tokens covers the specific structure that Auth0 and similar OAuth providers embed.

Offline environments benefit immediately. You’re debugging VPN-provided tokens on an air-gapped network. No internet means no online tools. CapyToolkit loads once, then works offline; no connectivity required. Disconnect after loading the page; your tokens stay private.

Documentation reviews move faster. API documentation often lists sample tokens;eyJh.... Instead of scrolling past encoded strings, decode them client-side to read claims instantly. Review scope and aud without leaving the documentation page.

Security-conscious teams mandate client-side inspection. For regulated data, keeping production tokens local reduces the number of third parties that receive sensitive identifiers.1 Offline decoding sidesteps compliance paperwork by eliminating transmission entirely.

Use client-side decoding as a trust-but-verify mechanism. It reveals what’s encoded, but verifying claims requires server-side validation. Decode locally, validate server-side; always.

Security Implications and Limitations

Client-side decoding is an inspection tool, not a security gate. It tells you what’s present in the token; claims, expiry, algorithms; but says nothing about integrity. Any browser extension or local script can Base64-decode a JWT; nothing prevents tampering or re-encoding. Don’t replace server-side validation with client-side comfort.

Signature verification remains server-side. Without the original key material; secrets for HMAC, public keys for RSA/ECDSA; the signature is meaningless. Libraries like jose enforce key rotation, JWKS endpoint caching, and algorithm allowlisting. Client-side inspection deliberately avoids this complexity to eliminate false confidence.

Clock skew distorts expiry checks. Local clocks rarely match server times. A token reported as “valid for another hour” might already be rejected by the server; or accepted despite appearing expired. Offline debugging complicates clock validation; prefer server-known timestamps during verification.

URL tokens leak via Referer headers. Storing JWTs in URLs (https://app/#token=...) transmits them when users click external links.7 This common anti-pattern shares tokens across contexts where they’re not intended. Always prefer Authorization: Bearer *** headers in API requests.

The absence of analytics is deliberate. CapyToolkit collects nothing; no token hashes, no claim statistics; eliminating another attack surface. This design choice means no tracking of usage patterns or error telemetry. When debugging production issues, servers remain the authoritative source.

Two-column comparison diagram showing what client-side JWT inspection reveals such as visible claims, algorithm, and expiry versus what server-side JWT verification enforces such as signature integrity, trusted issuer, and claim authorization
Client-side decoding tells you what is inside the token. Server-side verification is the step that confirms whether to trust it.

Offline-First Developer Workflows

You’re developing on a secure network segment with no internet access; common in healthcare, aerospace, or corporate APIs. The JWT Decoder & Claims Inspector loads once, then works offline. Disconnect Ethernet, disable Wi-Fi; tokens decode exactly as before. Nothing leaves the tab.

Air-gapped systems benefit immediately. Copy tokens from another machine, paste them into CapyToolkit, and inspect. The tool’s zero-network design guarantees nothing transmits. This workflow extends to sensitive systems where third-party tools are prohibited.

Reproducible debugging matters when ticketing. A QA engineer reports “Expiration time shows 2 hours ahead.” You reproduce locally; no clock drift, no timezone issues; because offline inspection uses your local clock. The bug was simply unclear label text.

CapyToolkit’s offline support is architectural. Pages load static HTML, JavaScript performs Base64 decoding and JSON parsing. Once cached, the tool survives reloads; ideal for intermittent connectivity. This engineered robustness keeps sensitive tokens strictly local. The same local-first design carries through CapyToolkit’s free collection of browser-based developer tools that require no account or upload.

Integration Tips for API Development

Use decoded claims to mock responses. You’re developing an API endpoint GET /user/profile expecting a JWT with sub and roles. Locally, mock the profile using decoded sub and roles. No server required; test authorization paths before writing real logic.

Validate token structure before implementing verification. Drop a JWT into CapyToolkit, confirm alg and aud match expectations. Mismatched aud breaks OAuth flows; catch this before deploying server-side middleware. For Google OAuth flows, the Google OAuth JWT inspector that verifies aud and iss claims locally in the browser makes this cross-check immediate.

Generate realistic test tokens. Decode production tokens once, replace sensitive claims with test values, then re-encode. This yields authentic JWTs covering edge cases; missing exp, unrecognized alg; that your verification logic must reject.

CapyToolkit complements jose and similar libraries. It’s inspection-only; server-side validation remains mandatory. Use it for understanding tokens during development, verifying documentation, and testing locally.

From Inspection to Implementation

Client-side inspection reveals the token’s anatomy: claims, algorithms, and expiry. Server-side validation enforces trust. The two phases work sequentially: decoding tells you what’s present; verification confirms its authenticity.

Start with signature validation. Your server fetches public keys from the issuer’s JWKS endpoint. Libraries like jose can resolve remote JWKS documents and cache them with cooldown behavior.9 If the signature doesn’t match, reject the token immediately. More subtly, ensure the token targets your API (aud: expected-audience) to prevent confused deputies.6

Next, enforce claims. Check exp using your server’s clock, and ignore local time deltas. Validate iss against a list of trusted issuers. For custom claims such as scope and roles, ensure they map to your authorization logic. Production systems often encode role-based access control (RBAC) in JWTs, and verifying these prevents elevation-of-privilege attacks.

Debugging happens naturally here. If scope: ["write:patients"] is unexpectedly missing, inspect the OAuth scope grants during login. Client-side decoding reveals whether the token ever included that scope; if not, the bug lives in the authorization flow.

Finally, monitor securely. Log a stable token identifier or hash, never raw JWTs. Hashes provide referential integrity without exposing credentials. Include sanitized sub, iss, and action. That is enough to connect client activities without leaking production tokens.

Decode locally, verify server-side. This sequence maximizes safety by eliminating risky transmission while enforcing strict server-side validation. CapyToolkit helps you decode locally, and libraries like jose and jsonwebtoken lock down server-side verification. When integrating token inspection into development workflows, CapyToolkit’s privacy-first philosophy guides decisions, as tools that respect user data by default eliminate complicated production reviews. Combine the JWT Decoder & Claims Inspector with other offline tools like the browser-based Base64 encoder and decoder to streamline cryptographic debugging.

Sources
  1. 1.

    M. Jones and D. Hardt, “The OAuth 2.0 Authorization Framework: Bearer Token Usage,” RFC 6750, IETF, October 2012. https://datatracker.ietf.org/doc/html/rfc6750

  2. 2.

    MDN Contributors, “Uint8Array.fromBase64(),” developer.mozilla.org, July 2025. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromBase64

  3. 3.

    Michael Jones, John Bradley, and Nat Sakimura, “JSON Web Token (JWT),” RFC 7519, IETF, May 2015. https://www.ietf.org/rfc/rfc7519.txt

  4. 4.

    Michael Jones, John Bradley, and Nat Sakimura, “JSON Web Signature (JWS),” RFC 7515, IETF, May 2015. https://www.ietf.org/rfc/rfc7515.txt

  5. 5.

    Michael Jones and John Hildebrand, “JSON Web Encryption (JWE),” RFC 7516, IETF, May 2015. https://www.rfc-editor.org/rfc/rfc7516

  6. 6.

    Yaron Sheffer, Dick Hardt, and Michael Jones, “JSON Web Token Best Current Practices,” RFC 8725, IETF, February 2020. https://www.rfc-editor.org/rfc/rfc8725

  7. 7.

    OWASP Foundation, “OAuth 2.0 Protocol Cheatsheet,” owasp.org, accessed June 2026. https://cheatsheetseries.owasp.org/cheatsheets/OAuth2_Cheat_Sheet.html

  8. 8.

    Michael Jones, Nat Sakimura, and John Bradley, “OAuth 2.0 Authorization Server Metadata,” RFC 8414, IETF, June 2018. https://datatracker.ietf.org/doc/html/rfc8414

  9. 9.

    Panva, “createRemoteJWKSet(),” github.com, accessed June 2026. https://github.com/panva/jose/blob/main/docs/jwks/remote/functions/createRemoteJWKSet.md

More in Developer Tools