โ† Back to Blog

Base64 Encoding in JWT Tokens

2026-04-18 ยท 5 min read

โ† Back to Blog

Base64 Encoding in JWT Tokens

ยท 5 min read

JWT's Three-Part Structure

JWT (JSON Web Token, RFC 7519) is a compact, self-contained way to securely transmit information between parties as JSON objects. A JWT consists of three Base64URL-encoded strings separated by dots (.): Header.Payload.Signature. Each part has a clear responsibility: the header describes the token type and signing algorithm, the payload contains claims (the data), and the signature verifies the token hasn't been tampered with.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Decoding the JWT Header

The JWT header is a JSON object encoded with Base64URL. A typical header contains two fields: alg (signing algorithm) and typ (token type, usually "JWT"). Decoding eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 produces {"alg":"HS256","typ":"JWT"}.

# ่งฃ็  JWT ๅคด้ƒจ / Decode JWT header
HEADER="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
# ๆทปๅŠ ๅกซๅ……ๅนถ่งฃ็  / Add padding and decode
echo "${HEADER}==" | base64 --decode
# {"alg":"HS256","typ":"JWT"}

# Python ่งฃ็  / Python decode
import base64, json

def decode_jwt_header(token):
    header_b64 = token.split('.')[0]
    header_b64 += '=' * (4 - len(header_b64) % 4)
    return json.loads(base64.urlsafe_b64decode(header_b64))

Decoding the JWT Payload

The JWT payload contains claims โ€” statements about an entity (usually a user) and additional data. The JWT spec defines some standard claims: sub (subject, usually user ID), iss (issuer), exp (expiration time, Unix timestamp), iat (issued at time), aud (audience). You can also add custom claims (like user roles, permission lists).

// ่งฃ็ ๅฎŒๆ•ด JWT๏ผˆไธ้ชŒ่ฏ็ญพๅ๏ผ‰
// Decode full JWT (without signature verification)
function decodeJWT(token) {
  const [headerB64, payloadB64, signature] = token.split('.');

  function b64UrlDecode(s) {
    s = s.replace(/-/g, '+').replace(/_/g, '/');
    s += '=='.slice(0, (4 - s.length % 4) % 4);
    return JSON.parse(atob(s));
  }

  return {
    header: b64UrlDecode(headerB64),
    payload: b64UrlDecode(payloadB64),
    signature: signature
  };
}

const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0In0.xxx";
const decoded = decodeJWT(jwt);
console.log(decoded.payload.sub); // "1234"

JWT Payload Is Not Encrypted

This is the most important security note about JWT: the JWT header and payload are only Base64URL-encoded, and anyone can decode and read their contents. JWT's security comes from signature verification, not data confidentiality. The signature ensures data hasn't been tampered with, but doesn't prevent data from being read.

Therefore, never store sensitive information (like passwords, credit card numbers, social security numbers) in JWT payloads! If you need to protect sensitive data in JWT, use JWE (JSON Web Encryption) rather than JWS (JSON Web Signature, i.e., regular JWT).

The Correct Way to Verify JWT Signatures

The JWT signature is the result of computing the header and payload (in Base64URL-encoded form) with the specified algorithm (like HMAC-SHA256), then Base64URL-encoding the result. Verifying the signature means: recalculating the signature with the same key and algorithm, then comparing with the signature in the JWT. If they match, the token is authentic and untampered.

# Python ้ชŒ่ฏ JWT๏ผˆไฝฟ็”จ PyJWT ๅบ“๏ผ‰
# Verify JWT in Python (using PyJWT library)
import jwt

SECRET_KEY = "your-256-bit-secret"

# ็”Ÿๆˆ JWT / Generate JWT
token = jwt.encode(
    {"sub": "user123", "role": "admin", "exp": 1735689600},
    SECRET_KEY,
    algorithm="HS256"
)

# ้ชŒ่ฏ JWT / Verify JWT
try:
    payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
    print(f"Valid! User: {payload['sub']}")
except jwt.ExpiredSignatureError:
    print("Token has expired")
except jwt.InvalidTokenError:
    print("Invalid token")

Common JWT Security Mistakes

(1) Setting algorithm to "none": early JWT libraries accepted alg: "none" (no signature), allowing attackers to forge arbitrary JWTs. Always explicitly specify allowed algorithms when verifying, and reject alg: "none". (2) RS256 public key as HMAC secret attack: when servers support both RS256 and HS256, attackers might change alg to HS256 and use the server's public key as the HMAC secret to forge tokens. Always enforce the expected algorithm when verifying.

(3) Storing sensitive data in the payload (emphasized above). (4) Using weak keys: HS256 keys should be sufficiently long (at least 256 bits), generated with strong randomness, avoiding simple strings like "secret". (5) Not verifying expiration: always verify the exp claim and reject expired tokens.

Debugging and Inspecting JWTs

When debugging JWT issues, the most commonly used tool is jwt.io (an online JWT decoding and verification tool). Pasting a JWT into jwt.io immediately shows the decoded header and payload contents, and (if a key is provided) signature verification results. Our Base64 tool can also be used to decode individual parts of a JWT (remember to remove the dots and other segments first).

In production environments, when encountering JWT-related errors, common debugging steps are: first decode the payload to check if exp (expiration time) and iss (issuer) are correct; check if the alg field matches the algorithm the server expects; confirm clock synchronization (JWT expiration checks depend on system clocks, and clock skew can cause issues).

Try the online tool now โ€” no installation, completely free.

Open Tool โ†’

Try the free tool now

Use Free Tool โ†’