Markdown (LLM)
Initializing...

JWT Decoder & Debugger

Decode, verify, and inspect JSON Web Tokens client-side. Check expiration timestamps and verify HMAC signatures in your browser.

Presets:

Inspecting and Validating JSON Web Tokens

Access tokens routinely carry tenant identifiers, user roles, permission scopes, and lifecycle timestamps (exp, nbf, iat). When testing token issuance pipelines or debugging authorization middleware, inspecting claims and verifying signatures helps pinpoint expiration mismatches and secret key misconfigurations.

This debugger parses tokens, decodes base64URL segments, formats header and payload JSON, and verifies HMAC signatures (HS256, HS384, HS512) directly in your browser using Go’s standard library.


Anatomy of a JSON Web Token

A JSON Web Token (RFC 7519) consists of three base64URL-encoded strings separated by periods:

text
1<Header> . <Payload> . <Signature>

1. Header

The header defines the metadata for the token, primarily the cryptographic algorithm used to generate the signature (alg) and the token type (typ):

json
1{
2  "alg": "HS256",
3  "typ": "JWT"
4}

2. Payload (Claims)

The payload contains the claims. Claims are statements about an entity (typically the user or service) along with additional metadata. Claims fall into three categories: registered, public, and private.

3. Signature

The signature ensures the token has not been tampered with in transit. For symmetric HMAC algorithms like HS256, it is computed by hashing the header and payload with a shared secret:

text
1HMACSHA256(
2  base64UrlEncode(header) + "." + base64UrlEncode(payload),
3  secret
4)

Registered Claims Reference

The JWT specification (RFC 7519) reserves seven standard claim keys. While none are strictly mandatory according to the specification, production authentication systems rely on them for session lifecycle management:

ClaimNameFormatDescription
issIssuerStringIdentifies the principal that issued the JWT (e.g. auth.lorbic.com)
subSubjectStringIdentifies the principal that is the subject of the JWT (e.g. user_9921)
audAudienceString or ArrayIdentifies the recipients that the JWT is intended for
expExpiration TimeNumericDate (Seconds)Timestamp on or after which the JWT must not be accepted
nbfNot BeforeNumericDate (Seconds)Timestamp before which the JWT must not be accepted
iatIssued AtNumericDate (Seconds)Timestamp at which the JWT was issued
jtiJWT IDStringUnique identifier for one-time token replay prevention

Common Production Vulnerabilities

When rolling out custom JWT validation in backend services, several well-known design flaws frequently appear in security audits:

The alg: none Bypass

Early JWT specifications allowed an algorithm value of none for unsigned tokens. If a backend verification library blindly trusts the header algorithm without validating it against an allowed list, an attacker can modify the payload (e.g. changing "role": "user" to "role": "admin"), set "alg": "none", strip the signature segment, and bypass authentication entirely.

Always enforce an explicit whitelist of acceptable signing algorithms in your backend verifier.

Key Confusion Attack (RS256 vs HS256)

If your authentication service issues tokens signed with an asymmetric private key (RS256) and exposes the corresponding public key for consumers to verify, an attacker might modify the token header to "alg": "HS256".

If the backend uses the same verification function for both algorithms and passes the RSA public key as the secret, HMAC will use the public key bytes as the symmetric secret. Because the RSA public key is known to everyone, the attacker can forge arbitrary valid tokens signed with that public key.

Clock Skew

Network time protocol (NTP) drift between authentication servers and API gateways can cause tokens to be rejected immediately after issuance or accepted shortly after expiration. Production token validators should always permit a configurable clock skew window (typically 30 to 60 seconds) when comparing exp and nbf timestamps.


Validating HMAC Signatures in Go

Here is how to verify an HS256 token using only the Go standard library, taking care to use constant-time byte comparisons to prevent timing attacks:

go
 1package main
 2
 3import (
 4	"crypto/hmac"
 5	"crypto/sha256"
 6	"crypto/subtle"
 7	"encoding/base64"
 8	"errors"
 9	"strings"
10)
11
12func VerifyHS256(token, secret string) error {
13	parts := strings.Split(token, ".")
14	if len(parts) != 3 {
15		return errors.New("invalid token format")
16	}
17
18	signingInput := parts[0] + "." + parts[1]
19	
20	mac := hmac.New(sha256.New, []byte(secret))
21	mac.Write([]byte(signingInput))
22	expectedSig := mac.Sum(nil)
23
24	// Decode incoming signature using unpadded base64URL
25	actualSig, err := base64.RawURLEncoding.DecodeString(parts[2])
26	if err != nil {
27		return errors.New("failed to decode signature")
28	}
29
30	// ConstantTimeCompare prevents side-channel timing attacks
31	if subtle.ConstantTimeCompare(expectedSig, actualSig) != 1 {
32		return errors.New("signature mismatch")
33	}
34
35	return nil
36}