JWT Decoder and Inspector
Decode JSON Web Tokens, inspect claims, check expiration, and validate token structure. Everything runs in your browser with zero data sent to any server.
| Claim | Value | Description |
|---|
Token Timeline
Signature
Understanding JSON Web Tokens
JSON Web Tokens (JWTs) are an open standard defined in RFC 7519 for securely transmitting information between parties as a compact, self-contained JSON object. I work with JWTs daily in authentication systems, API authorization, and single sign-on (SSO) implementations. This tool reflects the debugging workflow I use when inspecting tokens during development and production troubleshooting.
A JWT encodes claims as a JSON object that is digitally signed and optionally encrypted. The signature guarantees the token's integrity, meaning any modification to the header or payload invalidates the signature. This makes JWTs suitable for scenarios where trust must be established without making a database lookup on every request.
The Three Parts of a JWT
Every JWT consists of three parts separated by dots. The first part is the header, which contains metadata about the token itself. The header is a JSON object that typically includes two fields: alg (the signing algorithm, such as HS256, RS256, or ES256) and typ (the token type, almost always "JWT"). The header is Base64URL-encoded to form the first segment of the token string.
The second part is the payload, which contains the claims. Claims are statements about the user, session, or any other data the application needs to convey. The JWT specification defines seven registered claims (iss, sub, aud, exp, nbf, iat, jti), but applications can include any custom claims they need. The payload is also Base64URL-encoded. It is important to understand that Base64URL encoding is not encryption. Anyone who has the token can decode the payload and read its contents. Never store sensitive information like passwords or credit card numbers in JWT claims.
The third part is the signature, which is generated by taking the encoded header, the encoded payload, and a secret or private key, then applying the algorithm specified in the header. For HMAC-based algorithms (HS256, HS384, HS512), the signature uses a shared secret. For RSA-based algorithms (RS256, RS384, RS512), the signature uses a private key and can be verified with the corresponding public key. ECDSA algorithms (ES256, ES384, ES512) also use asymmetric key pairs but with elliptic curve cryptography.
Base64URL Encoding vs. Base64
JWTs use Base64URL encoding, which differs from standard Base64 in two ways. The + character is replaced with -, and the / character is replaced with _. This makes the encoded string safe for use in URLs, query parameters, and HTTP headers without requiring additional percent-encoding. Padding characters (=) are also stripped from the output.
When decoding a JWT manually, you need to reverse these substitutions before applying standard Base64 decoding. This tool handles the conversion automatically, but understanding the difference is important when debugging JWT issues in applications that use standard Base64 libraries without the URL-safe variant.
Registered Claims Explained
The JWT specification defines seven registered claim names that provide interoperable metadata. The iss (issuer) claim identifies who created and signed the token. In a multi-service architecture, this tells the receiving service which authentication server issued the token. The sub (subject) claim identifies the principal that is the subject of the JWT, typically a user ID or account identifier.
The aud (audience) claim identifies the recipients that the JWT is intended for. A token issued for API service A should not be accepted by API service B. The exp (expiration time) claim is a Unix timestamp after which the token must be rejected. The nbf (not before) claim is a Unix timestamp before which the token must not be accepted. The iat (issued at) claim records when the token was created. The jti (JWT ID) claim provides a unique identifier for the token, useful for preventing replay attacks.
All time-based claims (exp, nbf, iat) use Unix timestamps, which represent the number of seconds since January 1, 1970, at 00:00:00 UTC. This decoder converts these timestamps to human-readable dates and calculates the time remaining until expiration or the time elapsed since issuance.
Common JWT Signing Algorithms
The choice of signing algorithm has significant implications for security and architecture. HMAC-SHA256 (HS256) is the most common algorithm for simple applications. It uses a single shared secret for both signing and verification. This means the secret must be distributed to every service that needs to verify tokens, which can be a security concern in distributed systems.
RSA-SHA256 (RS256) uses asymmetric cryptography. The authentication server signs tokens with a private key that only it possesses. Any service can verify tokens using the corresponding public key, which can be freely distributed. This is the preferred algorithm for microservice architectures and third-party integrations because it eliminates the need to share secret keys.
ECDSA-SHA256 (ES256) offers similar benefits to RS256 but uses elliptic curve cryptography, which produces smaller signatures and faster verification with equivalent security. ES256 is increasingly popular in performance-sensitive environments and mobile applications where bandwidth and processing power are constrained.
The none algorithm means the token has no signature. This is only appropriate for tokens that have already been verified by another mechanism, such as within a trusted internal network. Accepting unsigned tokens in production is a critical security vulnerability. Many JWT libraries reject none by default, and your application should do the same.
JWT vs. Session Cookies
Traditional web applications use server-side sessions stored in a database or memory, with a session ID sent to the client as a cookie. The server looks up the session data on every request. JWTs take a different approach by storing the session data (claims) in the token itself, eliminating the need for server-side session storage.
The primary advantage of JWTs is statelessness. The server does not need to store or look up session data, which simplifies horizontal scaling. Multiple server instances can verify tokens independently without shared state. This makes JWTs well-suited for API authentication, mobile applications, and microservice architectures.
The primary disadvantage is that JWTs cannot be revoked before expiration without additional infrastructure. With server-side sessions, you can delete the session to log out a user immediately. With JWTs, you need a token blocklist (sometimes called a denylist) or short expiration times combined with refresh tokens to achieve similar behavior. The trade-off between statelessness and revocability is the central design decision when choosing between JWTs and sessions.
Refresh Token Pattern
Most JWT-based authentication systems use two tokens. The access token is short-lived (typically 5 to 60 minutes) and included in API requests for authorization. The refresh token is long-lived (days or weeks) and stored securely on the client, used only to obtain new access tokens when the current one expires.
When the access token expires, the client sends the refresh token to a dedicated endpoint on the authentication server. If the refresh token is valid and has not been revoked, the server issues a new access token. This pattern provides a balance between security (short-lived access tokens limit the damage if a token is compromised) and user experience (the user does not need to re-enter credentials every few minutes).
Refresh tokens should be stored in HTTP-only, secure, same-site cookies to prevent JavaScript access and cross-site request forgery. Access tokens are typically stored in memory and included in API requests via the Authorization header with the Bearer scheme.
JWT Security Considerations
Several well-documented attacks target JWT implementations. The algorithm confusion attack exploits applications that accept the algorithm specified in the token header without validation. An attacker might change the algorithm from RS256 to HS256 and sign the modified token using the RSA public key as the HMAC secret. Since the public key is often freely available, this allows token forgery. The defense is to always validate the algorithm on the server side and never trust the header's alg claim for algorithm selection.
The none algorithm attack involves removing the signature and setting the algorithm to none. Vulnerable libraries that accept unsigned tokens will process the forged payload. The defense is to reject tokens with the none algorithm in production and to explicitly specify which algorithms your application accepts.
Token leakage through URLs is another concern. JWTs passed as query parameters appear in browser history, server logs, referrer headers, and proxy logs. Always transmit JWTs in the Authorization header or in HTTP-only cookies, never in URLs. If a token is leaked, its short expiration time limits the window of exploitation.
Key management is critical for JWT security. HMAC secrets should be at least 256 bits of cryptographically random data. RSA keys should be at least 2048 bits. Rotate keys periodically and maintain a key ID (kid) in the JWT header so services can look up the correct key from a JSON Web Key Set (JWKS) endpoint during verification.
JWTs in OAuth 2.0 and OpenID Connect
OAuth 2.0 defines authorization flows that issue access tokens, and these tokens are often (but not always) JWTs. OpenID Connect (OIDC), built on top of OAuth 2.0, mandates the use of JWTs for ID tokens. The ID token contains claims about the authentication event and the authenticated user, including sub, name, email, and iss.
OIDC providers (Google, Microsoft, Auth0, Okta, and others) publish their public keys at a well-known JWKS endpoint, typically at /.well-known/jwks.json. Applications verify ID tokens by fetching the provider's public keys and matching the kid header claim to the correct key. This eliminates the need for shared secrets between the identity provider and the application.
When debugging OAuth or OIDC integrations, this decoder helps you inspect the contents of access tokens and ID tokens to verify that the correct claims are present, the issuer matches the expected provider, and the token has not expired. These are the most common issues I encounter when integrating third-party authentication.
JWT Size and Performance
Because JWTs are self-contained, they can become large if many claims are included. Each claim adds to the token size, and the token is sent with every API request. A typical JWT with standard claims is 300 to 500 bytes. Adding custom claims like user roles, permissions, and profile data can push the size to 1KB or more. Tokens embedded in the Authorization header count against the maximum header size limit, which varies by server (8KB for Apache, 8KB for Nginx by default).
To keep token size manageable, include only the claims that the API needs for authorization decisions. Store detailed user profile data in a database and fetch it when needed rather than embedding everything in the token. If you need to convey complex authorization data, consider using a reference token pattern where the JWT contains only a reference ID, and the API service looks up the full authorization data from a shared service.
Debugging JWT Issues
The most common JWT debugging scenarios involve expired tokens, clock skew, audience mismatch, and algorithm mismatch. When a user reports "401 Unauthorized" errors, the first step is to decode their token and check the exp claim. If the token has expired, the issue is in the token refresh logic on the client side.
Clock skew occurs when the server's system clock differs from the authentication server's clock. A token issued with an iat slightly in the future (from the verifying server's perspective) may be rejected. Most JWT libraries accept a small clock tolerance (typically 30 to 60 seconds) to handle this.
Audience mismatch happens when a token intended for one service is sent to a different service. The aud claim in the token must match the expected audience configured on the verifying service. This is a common issue in multi-service architectures where services are not configured with the correct audience values.
Algorithm mismatch occurs when the token is signed with one algorithm but the verifying service expects a different one. This typically happens during key rotation or when migrating between signing algorithms. The solution is to ensure all services are updated to accept the new algorithm before switching the authentication server to use it.
JWTs in Mobile Applications
Mobile apps commonly use JWTs for API authentication because they support stateless, expandable backends. On iOS, tokens should be stored in the Keychain. On Android, use the EncryptedSharedPreferences or the Android Keystore system. Avoid storing tokens in plain SharedPreferences, UserDefaults, or local storage, as these locations can be accessed by other apps on rooted or jailbroken devices.
Mobile apps face unique challenges with token expiration. Users may open the app after hours or days of inactivity, finding their access token expired. The app should handle this gracefully by attempting a silent token refresh using the refresh token. If the refresh token has also expired, redirect the user to the login screen. Network connectivity issues add another layer of complexity, as token refresh requests may fail when the device is offline.
Token Storage in Web Applications
In browser-based applications, token storage is a topic of ongoing debate. LocalStorage is accessible from JavaScript, making it vulnerable to cross-site scripting (XSS) attacks. If an attacker injects malicious JavaScript into the page, they can read the token from LocalStorage and send it to their server. HTTP-only cookies are not accessible from JavaScript, providing protection against XSS, but they require careful configuration of SameSite, Secure, and Domain attributes to prevent cross-site request forgery (CSRF).
The recommended approach for single-page applications (SPAs) is to store the access token in memory (a JavaScript variable) and the refresh token in an HTTP-only, Secure, SameSite="Strict" cookie. The access token is lost when the page is refreshed, but the refresh token in the cookie persists and can be used to obtain a new access token silently. This approach minimizes the attack surface while providing a reasonable user experience.
JWT Libraries and Implementations
Every major programming language has well-maintained JWT libraries. In Node.js, the jsonwebtoken package by Auth0 is the most popular, with jose as a more modern alternative that supports the full JOSE suite (JWS, JWE, JWK, JWKS). In Python, PyJWT handles encoding and decoding with algorithm validation. In Go, golang-jwt/jwt (formerly dgrijalva/jwt-go) provides type-safe JWT handling. In Java, the java-jwt library by Auth0 and the Nimbus JOSE+JWT library are widely used.
When selecting a JWT library, verify that it validates the algorithm claim against a whitelist, supports key rotation through JWKS, handles clock skew tolerance, and has an active maintenance history. Abandoned libraries may lack patches for newly discovered vulnerabilities. I maintain a policy of reviewing JWT library dependencies quarterly for known vulnerabilities using tools like npm audit, pip-audit, or Snyk.
The JWT Lifecycle
Understanding the full lifecycle of a JWT helps in debugging and system design. The lifecycle begins when a user authenticates (providing credentials, completing MFA, or using social login). The authentication server validates the credentials, generates the JWT with appropriate claims (user ID, roles, expiration), signs it with the server's private key or shared secret, and returns it to the client.
The client stores the token and includes it in subsequent API requests, typically in the Authorization: Bearer <token> header. Each API server that receives the request validates the token by checking the signature, verifying the expiration time, confirming the audience claim, and extracting the claims for authorization decisions. If the token is valid, the request is processed. If not, the server returns a 401 Unauthorized response.
When the token approaches expiration, the client uses a refresh token to obtain a new access token. The authentication server verifies the refresh token, checks that it has not been revoked, and issues a new access token. Some implementations also rotate the refresh token at this point, issuing a new refresh token and invalidating the old one. This refresh token rotation prevents stolen refresh tokens from being used indefinitely.
The lifecycle ends when the user logs out. The client discards the access token and sends the refresh token to a revocation endpoint. The authentication server adds the refresh token to a revocation list so it cannot be used to obtain new access tokens. Access tokens that have already been issued continue to be valid until they expire, unless the server maintains an access token blocklist as well.
JWT Header Parameters Beyond alg and typ
While alg and typ are the most common header parameters, the JWT specification and related RFCs define several additional parameters. The kid (Key ID) parameter identifies which key was used to sign the token. In systems with multiple signing keys or during key rotation, the kid allows the verifying service to select the correct key from a key set without trying each key sequentially.
The jku (JWK Set URL) parameter provides a URL where the verifying service can fetch the signer's public keys in JWK Set format. The jwk parameter embeds the public key directly in the header. The x5u parameter provides a URL to an X.509 certificate chain, and x5c embeds the certificate chain directly. These parameters are used in enterprise environments with certificate-based authentication infrastructure.
The cty (Content Type) parameter is used in nested JWTs, where a JWT is the payload of another JWT. Setting cty to "JWT" indicates that the payload should be decoded as another JWT. This nesting is rare in practice but appears in some enterprise identity federation scenarios.
JWT Claim Best Practices
When designing the claims structure for your application, follow several important principles. Keep the payload minimal by including only claims that the receiving service needs for its immediate authorization decisions. Each additional claim increases token size and exposes more data if the token is intercepted. Store detailed user profiles in your database and reference them by the sub claim rather than embedding all profile fields.
Use standard claim names whenever possible rather than inventing custom names for the same concept. Using sub for the user identifier, iss for the issuer, and roles for role-based access control is more interoperable than custom claim names like userId, issuedBy, or userRoles. Standard claim names are recognized by JWT libraries and tools, making debugging easier.
Namespace custom claims to avoid collisions with other services. If your application uses a custom claim for subscription tier, name it https://myapp.com/subscription or myapp.subscription rather than just subscription. This prevents conflicts when your tokens are consumed by third-party services that might define their own subscription claim.
Set reasonable expiration times based on the sensitivity of the operations the token authorizes. Tokens for reading public data might expire in 24 hours. Tokens for modifying user data should expire in 15 to 60 minutes. Tokens for administrative operations or financial transactions should expire in 5 to 15 minutes. Shorter expiration times reduce the window of opportunity if a token is compromised.
Cross-Origin JWT Authentication
When your frontend application and API are on different domains, JWT authentication requires careful handling of Cross-Origin Resource Sharing (CORS) headers. The API server must include Access-Control-Allow-Origin with the frontend's domain, Access-Control-Allow-Headers with "Authorization" included, and Access-Control-Allow-Methods with the HTTP methods your API supports.
For preflight requests (OPTIONS), the browser sends a preliminary request to check CORS permissions before sending the actual request with the JWT. Your API server must handle OPTIONS requests correctly and return the appropriate CORS headers. If the preflight fails, the browser blocks the actual request without sending the JWT, resulting in authentication errors that appear on the client as network failures rather than 401 responses.
When using cookies for JWT storage (which provides XSS protection), you need to configure the cookie with SameSite="None and Secure for cross-origin scenarios. This allows the cookie to be sent with cross-origin requests but only over HTTPS. The Access-Control-Allow-Credentials: true header must also be set on the API, and the frontend's fetch request must include credentials: 'include'.
JWT in Serverless and Edge Computing
JWTs are particularly well-suited for serverless architectures (AWS Lambda, Cloudflare Workers, Vercel Edge Functions) because they can be verified without database access. A serverless function that verifies JWTs only needs the public key or shared secret, which can be provided as an environment variable or fetched and cached from a JWKS endpoint.
Edge computing platforms process requests at locations geographically close to the user. Verifying a JWT at the edge takes microseconds, while making a database call to a centralized session store would add tens or hundreds of milliseconds of latency. This makes JWT-based authentication the natural choice for edge-first architectures where low latency is a priority.
For caching at the edge, the JWT's exp claim can inform cache duration decisions. An edge function might cache personalized content for a user until their token expires, then require re-authentication for the next request. This approach provides per-user caching without maintaining a centralized session database at each edge location.
Testing and Generating JWTs
During development, you often need to create test JWTs with specific claims and expiration times. Most JWT libraries provide encoding functions that accept a payload object, a signing key, and algorithm options. In Node.js with the jsonwebtoken package, creating a test token is as simple as calling jwt.sign({ sub: '123', role: 'admin' }, secret, { expiresIn: '1h' }).
For automated testing, generate tokens with known claims and verify that your application handles each scenario correctly. Test with expired tokens to verify 401 responses. Test with invalid signatures to verify rejection. Test with missing required claims to verify appropriate error messages. Test with the none algorithm to verify that your application rejects unsigned tokens. Test with tokens issued in the future (iat in the future) to verify clock skew handling.
Integration tests should verify the entire authentication flow, from login to token issuance to API access to token refresh to logout. Mock the authentication server to control the tokens it issues, allowing you to test edge cases like expired refresh tokens, revoked tokens, and algorithm changes without depending on an external service.
JWT Alternatives
While JWTs are the dominant token format for API authentication, alternatives exist for specific use cases. PASETO (Platform-Agnostic Security Tokens) is a more opinionated standard that eliminates the algorithm confusion vulnerability by not including the algorithm in the token itself. Instead, the algorithm is determined by the token version. PASETO v4 uses XChaCha20-Poly1305 for local (symmetric) tokens and Ed25519 for public (asymmetric) tokens.
Macaroons are another token format that supports caveats (conditions that restrict the token's use). Caveats can limit a token to specific IP addresses, time windows, or resource scopes, and anyone holding the token can add more restrictions (but cannot remove existing ones). This delegation model is useful for distributed systems where intermediate services need to pass tokens with reduced permissions.
Opaque tokens (random strings with no embedded data) remain common in traditional web applications. They require a server-side lookup on every request but support immediate revocation and do not expose any data if intercepted. The choice between JWTs and opaque tokens depends on whether statelessness or immediate revocability is more important for your application.
JSON Web Key Sets (JWKS)
A JSON Web Key Set (JWKS) is a JSON document that contains the public keys used to verify JWT signatures. The JWKS format is defined in RFC 7517 and is the standard mechanism for distributing signing keys in OAuth 2.0 and OpenID Connect implementations. The keys are typically served from a well-known endpoint such as https://auth.example.com/.well-known/jwks.json.
Each key in the JWKS includes a key ID (kid), key type (kty, such as RSA or EC), algorithm (alg), and the key material itself (for RSA keys, the modulus n and exponent e). When verifying a JWT, the application reads the kid from the token header, fetches the JWKS from the authentication server, finds the matching key, and uses it for signature verification.
JWKS endpoints should be cached by the verifying application to avoid making a network request on every token verification. A common caching strategy is to cache the JWKS for a fixed duration (e.g., 1 hour) and refresh it if a token arrives with a kid that is not in the cached key set. This handles key rotation gracefully while minimizing latency from JWKS fetches.
Key Rotation Strategies
Periodic key rotation is a security best practice that limits the impact of key compromise. The rotation process involves generating a new key pair, adding the new public key to the JWKS endpoint alongside the old key, switching the authentication server to sign tokens with the new private key, and eventually removing the old public key from the JWKS after all tokens signed with it have expired.
During the overlap period (when both old and new keys are in the JWKS), tokens signed with either key can be verified. The kid header parameter tells the verifying service which key to use. This ensures zero downtime during rotation. The overlap period should be at least as long as the maximum token lifetime to prevent valid tokens from becoming unverifiable.
Automated key rotation is supported by most identity platforms. Auth0 rotates signing keys automatically and provides APIs to trigger rotation on demand. AWS Cognito rotates keys periodically and publishes them at the JWKS endpoint. If you manage your own authentication server, implement a key rotation schedule (quarterly or semi-annually) and automate the process to reduce the risk of human error.
JWT in GraphQL APIs
GraphQL APIs use JWTs the same way REST APIs do, typically receiving the token in the Authorization header and verifying it before processing the query. The main difference is that GraphQL has a single endpoint, so authorization must be handled at the resolver level rather than the route level.
In a GraphQL context, the decoded JWT claims are typically attached to the request context object, making them available to all resolvers. Individual resolvers can then check the user's roles or permissions before returning data. Directive-based authorization (using custom GraphQL directives like @auth or @hasRole) provides a declarative way to protect specific fields and types based on JWT claims.
Subscription authentication in GraphQL adds complexity because WebSocket connections are long-lived. The JWT is typically sent during the WebSocket handshake (in the connection_init message) and verified once. Since the connection may outlive the token's expiration, some implementations re-verify the token periodically or require the client to send a new token when the current one expires.
Compliance and Regulatory Considerations
When JWTs contain personally identifiable information (PII) such as names, email addresses, or phone numbers, they fall under data protection regulations like GDPR, CCPA, and HIPAA. Since JWT payloads are only encoded (not encrypted), any intermediary that handles the token can read the PII. This has implications for logging, caching, and data retention policies.
To comply with data minimization principles, avoid including PII in JWT claims unless the receiving service specifically needs it. Use opaque identifiers (like user IDs) in the sub claim and let the receiving service look up PII from its own database when needed. If PII must be included in the token, consider using JWE (encrypted JWTs) to protect the payload contents from intermediaries.
Audit logging of JWT verification events (successful verifications, expired tokens, invalid signatures) helps meet compliance requirements for access monitoring. Include the jti claim in your tokens to provide a unique identifier for each token that can be correlated across log entries. This makes it possible to trace a specific authentication event through the system for audit purposes.
Data retention policies should address how long JWTs and their associated metadata are stored. Server access logs that contain JWTs in Authorization headers effectively store the full token for the log retention period. Configure your logging infrastructure to redact or truncate Bearer tokens from request headers before storing log entries. This prevents accidental exposure of token contents through log aggregation systems or log analytics platforms. Many web application firewalls (WAFs) and API gateways offer built-in header redaction features for this purpose.
Frequently Asked Questions
iss claim identifies the token issuer (who created it). The sub claim identifies the subject (who the token is about, typically a user ID). The aud claim identifies the audience (which service should accept the token). The exp claim sets the expiration time as a Unix timestamp. The nbf claim sets the earliest time the token is valid. The iat claim records when the token was issued. The jti claim provides a unique token identifier. Applications can also add custom claims for roles, permissions, email, name, and any other data needed.