Skip to content
Terug naar blog
Beveiliging

JWT-token decoderen: complete gids voor ontwikkelaars

Zo decodeer je een JWT-token veilig in de browser, Node.js, Python en Go. Bekijk header, payload en claims met een gratis online JWT decoder.

12 min leestijd

JWT-token decoderen: complete gids voor ontwikkelaars

Je API geeft 401 Unauthorized terug. De header Authorization: Bearer eyJhbGciOi... ziet er goed uit. Is het token verlopen, klopt de audience niet, of heeft de issuer een sleutel geroteerd? Die vraag kun je niet beantwoorden zonder te lezen wat er in het token staat — en om een JWT-token te decoderen heb je geen geheim, geen library en zelfs geen netwerkverbinding nodig. Een JWT bestaat uit drie base64url-geëncodeerde delen, gescheiden door punten. Decoderen is mechanisch: splitsen, base64url, JSON.parse. Geen magie, geen cryptografie.

Deze gids beschrijft de anatomie van een JWT, laat zien hoe je een JWT decodeert in Node.js, Python, Go en de browser, behandelt het verschil tussen decoderen en verifiëren — een onderscheid waar de meeste teams op struikelen — en somt de echte foutmodi op die je kunnen bijten. Als je nu meteen een token wilt inspecteren, ga dan direct naar onze gratis JWT decoder. Die draait volledig in je browser, zodat productie-tokens je apparaat nooit verlaten.

Wat is een JWT? (Snelle anatomie)

Een JSON Web Token (JWT) is een compacte, URL-veilige credential, gedefinieerd in RFC 7519. Het transporteert claims — data over de gebruiker en het token zelf — tussen twee partijen. Een JWT bestaat uit drie base64url-geëncodeerde delen, gescheiden door punten: een header, een payload en een signature.

Hier is een echt token opgesplitst zodat je de structuur kunt zien:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9       ← header
.
eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTk5OTk5OTk5OX0   ← payload
.
4NhxPjwoZxPNuxG-2C5ugGxaUsUJ0QyskAz7Ymz5Sg0       ← signature

De header beschrijft hoe het token ondertekend is, doorgaans { "alg": "HS256", "typ": "JWT" }. De payload bevat de claims: geregistreerde zoals sub, exp, iat plus aangepaste zoals role of tenant. De signature is een cryptografisch bewijs, berekend over de header en payload, waarmee de ontvanger kan detecteren of er geknoeid is. Base64url is een URL-veilige variant van Base64; bekijk onze beginnersvriendelijke Base64-gids voor een introductie van tien minuten.

Je komt JWT’s overal tegen waar moderne authenticatie leeft: OAuth 2.0 access tokens, OpenID Connect ID tokens, API-credentials gegenereerd door Auth0, Okta, Clerk, Supabase, Firebase, en tokens die tussen microservices in een mesh worden doorgegeven. Het is het standaard credential-formaat van het afgelopen decennium.

Voordat we verder gaan, is er één zin die je moet internaliseren: JWT’s zijn geëncodeerd, niet versleuteld. Iedereen die het token bezit, kan elke claim lezen. De signature bewijst herkomst; hij verbergt de inhoud niet. Dat ene feit bepaalt wat volgt: wat veilig is om in de payload te zetten, waarom decoderen geen geheim vereist, en waarom signatureverificatie op de server niet onderhandelbaar is.

Hoe JWT-decodering werkt (base64url, geen ontsleuteling)

Een JWT decoderen is geen cryptografische operatie. Het zijn vier mechanische stappen:

  1. Splits het token op . in precies drie segmenten.
  2. Decodeer het eerste segment met base64url en lees het als JSON. Dat is de header.
  3. Decodeer het tweede segment met base64url en lees het als JSON. Dat is de payload.
  4. Laat het derde segment (de signature) als ruwe bytes staan. Verificatie vereist de sleutel.

Dat is het hele algoritme. Geen library verplicht. Elke taal met Base64 en een JSON-verwerker kan een JWT decoderen in vijf regels. Onze Base64-encoder/decoder voert stap 2 en 3 handmatig uit als je de mechanica zelf wilt zien.

Wat is base64url?

Base64url is gewone Base64 met drie aanpassingen zodat de uitvoer veilig is in URL’s en HTTP-headers: - vervangt +, _ vervangt /, en het afsluitende =-opvulling wordt weggelaten. Als je ruwe base64url zonder die omzettingen terug in een standaard Base64-decoder gooit, krijg je ruis of een fout. De uitgebreide Base64-gids behandelt de opvullings-randgevallen uitgebreid.

Standaard Base64Base64url
AlfabetA-Z a-z 0-9 + /A-Z a-z 0-9 - _
OpvullingVerplicht = aan het eindeWeggelaten
URL-veilig?NeeJa
VoorbeeldPDw/Pz8+PDw_Pz8-

Nog één ding om hardop te zeggen: je kunt de signature niet client-side ontsleutelen. Decoderen is eenrichtingsverkeer: van geëncodeerde bytes naar JSON. De signature verifiëren is een afzonderlijke operatie waarvoor je ofwel het HMAC-geheim nodig hebt (voor HS-familie-algoritmen) of de publieke sleutel van de issuer (voor RS, PS, ES, EdDSA).

Waarom je het geheim niet nodig hebt om te decoderen

Omdat de payload base64url plus JSON is, geen versleutelde tekst. Een geheim komt pas in beeld als je wilt bewijzen dat er niet met het token geknoeid is — dat is een signaturecontrole. Iedereen op het netwerkpad, iedereen die het token in een logbestand heeft, iedereen met een browser kan elke claim lezen die je erin hebt gezet. Daarom mag je nooit wachtwoorden, API-sleutels of persoonsgegevens verder dan wat de ontvanger al weet in een JWT-payload stoppen. Voor het bredere dreigingsmodel, lees onze gids met aanbevolen aanpak voor beveiliging.

JWT online decoderen in 3 klikken: gratis JWT decoder

Soms heb je gewoon nu meteen een antwoord nodig. Is dit token verlopen, klopt de aud-claim, staat er alg:none in de header? De snelste weg is onze online JWT decoder. Die is gebouwd voor incident-response om 2 uur ‘s nachts.

  1. Plak het volledige token in het invoerveld. Zorg dat je alle drie de door punten gescheiden segmenten meepakt.
  2. Lees de gedecodeerde header, payload en de statuschips bovenaan: algoritme, uitgiftedatum, vervaldatum en een rode Expired-badge als exp al in het verleden ligt.
  3. Kopieer het paneel dat je nodig hebt naar je bugrapport, Slack-thread of testfixture.

Waarom het veilig is voor echte productie-tokens:

  • 100% in de browser. Alles draait lokaal via de ingebouwde atob en JSON.parse. Nooit een netwerkverzoek.
  • Geen logging, geen tracking, geen cookies, geen account nodig.
  • Werkt offline zodra de pagina geladen is.

De JWT Decoder-tool is algoritme-agnostisch. Omdat decoderen alleen base64url en JSON vereist, leest hij elke JWS-variant: HS256/384/512, RS256/384/512, PS256/384/512, ES256/384/512, EdDSA en alg:none. Alleen signatureverificatie is afhankelijk van het algoritme, en verificatie is niet iets wat je via een publieke webtool wilt doen. Meer daarover zo.

Wil je een segment handmatig met Base64 decoderen om de tool te controleren? Gebruik onze Base64-encoder/decoder en geef elk segment als base64url mee.

JWT decoderen in code (Node.js, Python, Go, browser)

Voor alles wat geen interactieve debugging is — middleware, tests, migratiescripts, CLI-tools — grijp je naar een library. Hier is de minimale code om een JWT te decoderen in de vier omgevingen die je het meest tegenkomt, met zowel het alleen-lezen pad als het verificatiepad naast elkaar. Elk fragment is klaar om te plakken en produceert de uitvoer die in de comments staat.

JWT decoderen in Node.js (jsonwebtoken)

// npm install jsonwebtoken
const jwt = require('jsonwebtoken');

const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' +
              '.eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTk5OTk5OTk5OX0' +
              '.4NhxPjwoZxPNuxG-2C5ugGxaUsUJ0QyskAz7Ymz5Sg0';

// Decode only — does NOT verify the signature
const decoded = jwt.decode(token, { complete: true });
console.log(decoded.header);   // { alg: 'HS256', typ: 'JWT' }
console.log(decoded.payload);  // { sub: 'user_123', exp: 1999999999 }

// Verify — the production path
const secret = process.env.JWT_SECRET;
const verified = jwt.verify(token, secret, { algorithms: ['HS256'] });

Geef altijd een expliciete algorithms-allowlist mee aan verify. Het weglaten ervan heeft aanvallers historisch in staat gesteld een RS256-token te downgraden naar HS256 door de publieke sleutel als HMAC-geheim te gebruiken — de klassieke algorithm-confusion-aanval. De allowlist is je verdediging.

JWT decoderen in Python (PyJWT)

# pip install PyJWT
import jwt

token = (
    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
    ".eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTk5OTk5OTk5OX0"
    ".4NhxPjwoZxPNuxG-2C5ugGxaUsUJ0QyskAz7Ymz5Sg0"
)

# Decode only — unsafe for auth, fine for inspection
decoded = jwt.decode(token, options={"verify_signature": False})
print(decoded)  # {'sub': 'user_123', 'exp': 1999999999}

# Header without touching the payload
header = jwt.get_unverified_header(token)
print(header)   # {'alg': 'HS256', 'typ': 'JWT'}

# Verify — production path
payload = jwt.decode(
    token,
    key="your-hs256-secret",
    algorithms=["HS256"],
    audience="api.example.com",
)

PyJWT weigert te verifiëren zonder een algorithms-lijst — een verstandige standaard die dezelfde confusion-aanval voorkomt waar het Node-voorbeeld voor waarschuwt.

JWT decoderen in Go (golang-jwt/jwt/v5)

// go get github.com/golang-jwt/jwt/v5
package main

import (
    "fmt"
    "github.com/golang-jwt/jwt/v5"
)

func main() {
    tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +
        ".eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTk5OTk5OTk5OX0" +
        ".4NhxPjwoZxPNuxG-2C5ugGxaUsUJ0QyskAz7Ymz5Sg0"

    // Decode only
    parser := jwt.NewParser()
    claims := jwt.MapClaims{}
    _, _, err := parser.ParseUnverified(tokenString, claims)
    if err != nil {
        panic(err)
    }
    fmt.Println(claims["sub"], claims["exp"]) // user_123 1.999999999e+09

    // Verify
    secret := []byte("your-hs256-secret")
    token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
        if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected alg: %v", t.Header["alg"])
        }
        return secret, nil
    })
    fmt.Println(token.Valid, err)
}

De keyFunc-closure is waar je de algoritme-familie afdwingt. Wijs alles af dat niet de verwachte methode is, vóórdat je de sleutel teruggeeft.

JWT decoderen in de browser (zonder dependencies)

Soms wil je helemaal geen dependency: een snel debug-paneel, een browserextensie, een kleine UI-badge die de huidige rol van de gebruiker toont. Ingebouwde browser-API’s zijn genoeg.

function decodeJwt(token) {
  const [h, p] = token.split('.');
  const pad = (s) => s + '==='.slice((s.length + 3) % 4);
  const decodeSegment = (s) => {
    const b64 = pad(s).replace(/-/g, '+').replace(/_/g, '/');
    const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
    return JSON.parse(new TextDecoder().decode(bytes));
  };
  return { header: decodeSegment(h), payload: decodeSegment(p) };
}

const { header, payload } = decodeJwt(token);
console.log(header);   // { alg: 'HS256', typ: 'JWT' }
console.log(payload);  // { sub: 'user_123', exp: 1999999999 }

De TextDecoder-stap is belangrijk voor elk token met een niet-ASCII-payload (emoji in een weergavenaam, Cyrillisch in een preferred_username). Plain atob geeft een binaire string terug, die JSON.parse laat mislukken bij multi-byte UTF-8. Dit is precies wat onze online JWT decoder lokaal in je browser uitvoert, zonder de UI.

Vergelijkingstabel

TaalAlleen decoderenVerifiërenLibrary
Node.jsjwt.decode(token)jwt.verify(token, key, { algorithms: [...] })jsonwebtoken
Pythonjwt.decode(token, options={"verify_signature": False})jwt.decode(token, key, algorithms=[...])PyJWT
Goparser.ParseUnverified(token, claims)jwt.Parse(token, keyFunc)golang-jwt/jwt/v5
Browseratob + TextDecoder + JSON.parseVraag je backend

Decoderen versus verifiëren: het cruciale verschil

Een JWT decoderen leest de claims; een JWT verifiëren bewijst dat die claims niet zijn aangepast. Decoderen vertrouwt nooit; verificatie is het vertrouwen. Dit is het ene onderscheid dat een werkende auth-implementatie scheidt van een CVE.

DecoderenVerifiëren
Geheim/sleutel nodig?NeeJa
Client-side uitvoeren?VeiligNooit
Bewijst authenticiteit?NeeJa
Controleert vervaldatum?OptioneelJa
GebruikDebuggen, inspecterenAuthenticatie, autorisatie

Neem nooit een autorisatiebeslissing op basis van een gedecodeerde (niet-geverifieerde) JWT. Niet in middleware, niet in een React-hook, niet in een serverloze functie die je denkt dat achter de gateway zit. Gedecodeerde claims zeggen wat het token beweert; geverifieerde claims zeggen wat de issuer heeft ondertekend. Een aanvaller die je server een handgemaakt token geeft zonder geldige signature kan alles in de payload zetten wat hij wil — en alleen de signaturecontrole verwerpt het.

Nog één detail over de HMAC-familie: als je HS256 gebruikt, is de entropie van je geheim het enige wat telt. Een kort, te raden geheim wordt offline brute-forced tegen elk token dat een aanvaller onderschept, waarna hij eigen tokens aanmaakt en zo naar binnen loopt. Gebruik minimaal 256 bits echte willekeurigheid. Zie onze gids over HMAC-geheimsterkte voor de rekensom achter dit belang.

Referentie voor veelvoorkomende JWT-claims

Elk JWT dat je tegenkomt, gebruikt een deel van de geregistreerde claims uit RFC 7519. Leer de korte lijst uit je hoofd:

ClaimNaamVoorbeeldOpmerkingen
issIssuerhttps://auth.example.comWie het token heeft aangemaakt
subSubjectuser_123Meestal de gebruikers-ID
audAudienceapi.example.comVoor wie het token bedoeld is — moet overeenkomen op de server
expExpiration1715003600Unix-seconden; verleden = verlopen
iatIssued At1715000000Unix-seconden waarop het token is aangemaakt
nbfNot Before1715000060Vroegste tijdstip waarop het token bruikbaar is
jtiJWT IDd1f8…Uniek per token; voorkomt replay
kidKey ID (header)key-2025-01Welke sleutel in je JWKS dit heeft ondertekend

Applicatiespecifieke claims staan hiernaast: role, scope, email, tenant_id, wat je identiteitsprovider ook uitgeeft. Houd ze kort. Elke byte gaat mee op elk verzoek.

Om leesbare datums te krijgen van iat en exp, gebruik je onze Unix timestamp-omrekentool. Plak het getal, krijg de datum in je lokale tijdzone, en herken een skew-bug in een seconde.

Probleemoplossing: waarom wil mijn JWT niet decoderen?

Vijf echte foutmodi, in ruwe volgorde van frequentie. Elke fout is geformuleerd als Symptoom → Oorzaak → Oplossing.

  1. “Invalid JWT format, expected three segments.” Je hebt alleen de payload gekopieerd, of de shell heeft het token over meerdere regels afgebroken en je hebt alleen de eerste regel gepakt. Oplossing: kopieer de volledige xxx.yyy.zzz-waarde opnieuw uit de originele response body, niet uit een gerenderd terminal. Lange waarden op één regel overleven beter in het Network-tabblad van browserdevtools dan in een gescrolde terminal.
  2. Vijf segmenten in plaats van drie. Je hebt een JWE (versleutelde JWT), geen JWS. Het formaat is header.encryptedKey.iv.ciphertext.tag. Een decoder leest de header, maar de payload is versleutelde tekst. Oplossing: de payload decoderen vereist de ontsleutelingssleutel, die normaal server-side door je auth-SDK wordt afgehandeld, niet door een debugtool.
  3. Base64url-fout bij een verder geldig uitziend token. Het token is ergens in het kopieerpad URL-geëncodeerd (een cookie, een redirect-URL, een onderschept proxylogboek). Je ziet letterlijk %2E of %2B in de string. Oplossing: decodeer het eerst via URL en geef het resultaat aan de JWT decoder.
  4. JSON-verwerkingsfout in de payload. Een terminal of chatclient heeft regelomslagpunten ingevoegd, of een script heeft slimme aanhalingstekens rondom een identifier geplakt. Oplossing: bekijk de ruwe response-bytes (curl met -o file.txt, of de Raw-weergave van devtools), verwijder witruimte en plak opnieuw.
  5. Decodeert prima, maar de backend weigert het nog steeds. Geen decoderingsprobleem, een verificatieprobleem. Het token is structureel geldig; iets wat de server controleert (signature, aud, exp, klokafwijking, kid-opzoekactie) mislukt. Ga naar de volgende sectie.

Twee gevallen die het vermelden waard zijn — geen verwerkingsfouten, maar de moeite waard om op te pakken terwijl je de decoder open hebt: een alg-waarde van none in de header (behandel als vijandig in productie) en een exp-waarde in het verleden. De decoder toont de claims toch zodat je kunt debuggen — dat is correct gedrag — en onze tool markeert het met een rode Expired-badge.

Wanneer decoderen niet genoeg is: signatureverificatie

Decoderen eindigt bij “dit is wat het token beweert.” Verificatie is wat een bewering omzet in een vertrouwensbeslissing. De signature is een bewijs, berekend met de privésleutel of het gedeelde geheim van de issuer, dat header en payload aan elkaar koppelt. Verander één byte en de signaturecontrole mislukt. Zonder die controle kan iedereen die naar je endpoint kan POST-en een “admin”-token fabriceren door de payload aan te passen en de signature over te slaan.

Accepteer nooit alg:none.

Productieverificatie ziet er in elke taal en elk framework ruwweg zo uit. Behandel ontbrekende items als bugs:

  • Geef een expliciete algorithms: ['RS256'] (of wat je gebruikt) allowlist mee. Dit weerhoudt algorithm-confusion-aanvallen.
  • Verifieer dat aud overeenkomt met de identifier van je service en dat iss overeenkomt met de verwachte issuer-URL.
  • Controleer exp aan de hand van de huidige tijd met maximaal 60 seconden klokafwijkingstolerantie.
  • Zoek bij sleutelrotatie de publieke sleutel op via kid uit een JWKS-endpoint. Sla nooit één sleutel voor altijd vast in de broncode.
  • Revoceer effectief door exp kort te houden (minuten, geen dagen) en optioneel een jti-denylist bij te houden voor tokens met hoge waarde.

Elke gangbare JWT-library biedt deze opties via één aanroep. Als je verificatiecode ze niet instelt, laat je standaardinstellingen staan — en standaardinstellingen zijn historisch de bron van bugs. Voor het volledige dreigingsmodel, zie onze gids met aanbevolen aanpak voor beveiliging.

FAQ

Kan ik een JWT decoderen zonder de geheime sleutel?

Ja. De header en payload zijn base64url-geëncodeerd, niet versleuteld, dus iedereen die het token bezit, kan de claims lezen. De geheime of publieke sleutel is alleen nodig om de signature te verifiëren. Dit is bewuste keuze: de payload is bedoeld om leesbaar te zijn zodat de ontvanger autorisatiebeslissingen kan nemen.

Is het veilig om mijn productie-JWT in een online decoder te plakken?

Alleen als de decoder in je browser draait en het token nooit uploadt. Onze JWT Decoder verwerkt alles lokaal met de ingebouwde atob en JSON.parse; er gaat niets naar een server. Remote debuggers die je token naar een API POST-en, moet je als credential-lek behandelen.

Wat is het verschil tussen een JWT decoderen en verifiëren?

Decoderen leest alleen de claims. Het heeft geen sleutel nodig en bewijst niets. Verifiëren controleert de signature aan de hand van de sleutel van de issuer en bevestigt dat er niet met het token is geknoeid. Neem nooit een authenticatiebeslissing op basis van een gedecodeerde maar niet-geverifieerde token.

Wat telt als geldig JWT-formaat?

Een geldige JWT heeft precies drie base64url-segmenten, gescheiden door punten: header.payload.signature. Vijf segmenten betekent dat je een versleutelde JWT (JWE) hebt, geen JWS. Nul punten betekent dat je slechts één segment hebt gekopieerd uit een afgebroken terminalregel.

Waarom toont de decoder een verlopen token nog steeds?

Een decoder leest de claims ongeacht geldigheid, zodat je afwijzingen kunt debuggen. Alleen een verifier weigert verlopen tokens. Onze tool toont een Expired-badge door exp te vergelijken met je lokale klok, zodat je het probleem direct herkent zonder naar Unix-tijdstempels te hoeven staren.

Welke algoritmen kan ik decoderen?

Alle. Decoderen heeft alleen base64url en JSON-verwerking nodig, dus het is algoritme-agnostisch. Dat geldt voor HS256/384/512, RS256/384/512, PS256/384/512, ES256/384/512, EdDSA en alg:none. Alleen verificatie is afhankelijk van het gekozen algoritme.

Moet ik jwt-decode of jsonwebtoken gebruiken in Node.js?

Gebruik jwt-decode aan de frontend als je alleen de payload wilt lezen — bijvoorbeeld om een gebruikersnaam uit een access token te tonen. Gebruik jsonwebtoken aan de backend, want alleen de backend kan de ondertekeningssleutel bewaren en jwt.verify uitvoeren. Verifieer nooit aan de client.

Conclusie

Een JWT decoderen is minder mysterieus dan “cryptografisch token” suggereert. Vijf punten om te onthouden, en je staat nooit meer voor een raadselachtige eyJhbGciOi…-string:

  • Decoderen is base64url plus JSON-verwerking. Geen geheim nodig.
  • Een JWT bestaat uit drie delen (header, payload, signature), gescheiden door punten.
  • Decoderen bewijst nooit authenticiteit. Verifieer altijd server-side met de sleutel van de issuer.
  • Wijs alg:none af en geef altijd een expliciete algoritme-allowlist mee aan verify.
  • Sla nooit wachtwoorden, privésleutels of gevoelige persoonsgegevens op in de payload. Die zijn leesbaar voor iedereen die het token bezit.

Bookmark onze gratis JWT decoder voor gebruik tijdens piketdienst. Plak een token, lees de claims en herken vervaldatums in een seconde — zonder dat je token de browser verlaat.

Gerelateerde artikelen

Alle artikelen bekijken