Skip to content

JWT Debugger — Python source

Decode a JSON Web Token and inspect its header and payload as pretty JSON, with exp/iat claim awareness and an expiry badge. Runs entirely in your browser.

This is the Python implementation — the same logic the interactive tool runs, in a shareable, citable form.

"""JWT base64url decoding helper — Python port.

Language: Python
CosmoDev polyglot showcase port of the `jwt` tool.
Ported from src/lib/jwt.ts.

Display source — part of CosmoDev's polyglot tool pages
(dev.cosmolabs.org).

Decode-only: JWT signature verification stays server-side; the page never
holds the secret. The single helper below converts a base64url string (the
alphabet used by every JWT header/payload segment) into text.
"""

import base64


def b64url_decode(s: str) -> str:
    """Decode a base64url string into text.

    JWT segments use RFC 4648's URL-safe base64 alphabet: ``-`` and ``_``
    replace ``+`` and ``/``, and the trailing ``=`` padding is usually omitted.

    Python's ``base64.urlsafe_b64decode`` understands the URL-safe characters
    but, by default, *silently discards* anything outside the alphabet — so we
    drive the standard decoder ourselves with validation on:

      1. map the URL-safe characters back to ``+`` / ``/``,
      2. restore the stripped ``=`` padding so the length is a multiple of 4, then
      3. decode with ``validate=True`` so any non-alphabet character raises
         ``binascii.Error`` (matching the TS throw on malformed input).

    The decoded bytes are interpreted as Latin-1 (``iso-8859-1``): each byte
    maps directly to the Unicode code point of the same value, mirroring the
    Web ``atob`` binary-string convention so the full 0x00-0xFF byte range
    round-trips. A UTF-8 decode would reject the 0x80-0xFF test vectors.

    Args:
        s: a base64url segment (e.g. a JWT header or payload).

    Returns:
        The decoded string.

    Raises:
        binascii.Error: if *s* contains characters outside the base64url
            alphabet.
    """
    # 1. URL-safe alphabet -> standard base64 alphabet that b64decode validates.
    standard = s.translate(str.maketrans("-_", "+/"))

    # 2. Restore stripped '=' padding so the length is a multiple of 4.
    padding = len(standard) % 4
    if padding:
        standard += "=" * (4 - padding)

    # 3. Strict decode — validate=True rejects anything outside the alphabet.
    data = base64.b64decode(standard, validate=True)

    # 4. Bytes -> text, byte-faithful (Latin-1), mirroring JS atob.
    return data.decode("latin-1")

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →