How to Decode JWT Payloads using Base64

Published: 2023-11-28 | Category: Web & API

JSON Web Tokens (JWT) are the modern standard for securing REST APIs and handling stateless user authentication. If you have ever looked at a JWT, you know it consists of three long strings of random-looking characters separated by periods (.).

Those strings are not encrypted; they are simply Base64URL encoded JSON strings.

The Structure of a JWT

A JWT is composed of three parts:

  1. Header: Contains metadata about the token (e.g., algorithm used).
  2. Payload (Claims): Contains the actual data (e.g., user ID, email, expiration time).
  3. Signature: A cryptographic hash used by the server to verify the token hasn't been tampered with.

Format: Header.Payload.Signature

Why Base64URL?

JWTs are designed to be passed in HTTP Authorization headers or as URL query parameters. Standard Base64 uses the + and / characters, which have special meanings in URLs and can cause parsing errors.

To solve this, JWTs use Base64URL encoding, which replaces + with - (minus) and / with _ (underscore). Additionally, JWTs usually strip the trailing = padding characters to save space.

Decoding a JWT in JavaScript (Without a Library)

While backend servers must cryptographically verify the signature using libraries, frontend applications often just need to read the Payload to display the user's name or check the expiration time. You can do this natively in the browser.

JavaScript
function decodeJWTPayload(token) {
    // 1. Split the token into its three parts
    const parts = token.split('.');
    if (parts.length !== 3) {
        throw new Error('Invalid JWT format');
    }

    // 2. Get the payload (the second part)
    let payloadStr = parts[1];

    // 3. Convert from Base64URL to standard Base64
    payloadStr = payloadStr.replace(/-/g, '+').replace(/_/g, '/');

    // 4. Decode the Base64 string to a JSON string
    // (Using atob is generally safe here as JWT claims are usually standard ASCII)
    const jsonString = atob(payloadStr);

    // 5. Parse the JSON string into a JavaScript object
    return JSON.parse(jsonString);
}

const myToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + 
                "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoxNTE2MjM5MDIyfQ." + 
                "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";

const userData = decodeJWTPayload(myToken);
console.log(userData.name); // Output: John Doe
console.log(userData.exp);  // Output: 1516239022

Important Security Warning

Decoding a JWT on the frontend does not verify its authenticity. Any user can open their browser's developer tools, modify the Base64 payload (e.g., changing "admin": false to "admin": true), and re-encode it.

However, because they do not have the server's secret cryptographic key, they cannot generate a valid Signature for their forged payload. When they send the forged token to the backend, the server's cryptographic verification will fail, and the request will be rejected.

Rule of Thumb: Frontends decode JWTs for UI convenience. Backends verify JWTs for authorization.

Handling JWT Padding Errors

If you are decoding JWTs in stricter languages (like Python or Java), the built-in decoders might throw an error because the JWT stripped the = padding.

To fix this, you must calculate the missing padding and append it before decoding:

Python
import base64
import json

def decode_jwt_payload(token):
    payload_segment = token.split('.')[1]
    
    # Calculate missing padding
    padding = 4 - (len(payload_segment) % 4)
    if padding and padding < 4:
        payload_segment += "=" * padding
        
    # Decode using the URL-safe method
    json_bytes = base64.urlsafe_b64decode(payload_segment)
    return json.loads(json_bytes.decode('utf-8'))

Conclusion

JWTs are nothing more than cleverly structured Base64URL strings protected by a cryptographic hash. By understanding how to replace URL-safe characters and handle missing padding, you can easily inspect token claims in any language.

Want to inspect a token quickly? Paste the payload segment into our Base64 Decoder tool.