6 min

Wat is een JWT? Een duidelijke gids voor JSON Web Tokens

Leer wat een JWT (JSON Web Token) is, hoe de drie onderdelen werken, waar het wordt gebruikt en belangrijke beveiligingstips om veelvoorkomende tokenfouten te vermijden.

Wat is een JWT? Een duidelijke gids voor JSON Web Tokens

JWT in eenvoudige bewoordingen

Een JWT (JSON Web Token) is een compacte, URL-veilige string die een set informatie vertegenwoordigt (meestal over een gebruiker of sessie) op een manier die tussen systemen kan worden doorgegeven. Je ziet het vaak als een lange waarde die begint met iets als eyJ..., meegestuurd in een HTTP-header zoals Authorization: Bearer \u003ctoken\u003e.

Waarom überhaupt een token gebruiken?

Traditionele logins vertrouwen vaak op server-sessies: nadat je hebt ingelogd, slaat de server sessiegegevens op en geeft de browser een session-ID-cookie. Elke aanvraag bevat die cookie en de server zoekt de sessie op.

Met tokengebaseerde authenticatie kan de server vermijden om voor elke gebruiker state bij te houden. In plaats daarvan houdt de client een token (zoals een JWT) en voegt dat toe aan API-aanroepen. Dit is populair voor API's omdat het:

  • goed werkt over meerdere diensten heen (API-gateways, microservices)
  • past bij mobiele apps en single-page apps (SPA's) die direct API's aanroepen
  • de noodzaak voor gedeelde sessieopslag tussen servers vermindert

Belangrijke nuance: “stateless” betekent niet “nooit server-side controles”. Veel echte systemen valideren tokens nog steeds tegen gebruikersstatus, roterende sleutels of intrekkingsmechanismen.

Authenticatie vs autorisatie (in gewone taal)

  • Authenticatie beantwoordt de vraag: Wie ben jij? (Je logt in en bewijst je identiteit.)
  • Autorisatie beantwoordt: Wat mag je doen? (Je mag facturen lezen, projecten bewerken, admin-pagina's openen, enz.)

JWT's dragen vaak bewijs van authenticatie (je bent ingelogd) en basis hints voor autorisatie (rollen, permissies, scopes) — maar je server moet toch autorisatieregels afdwingen.

Waar zie je JWT's terug?

Je ziet JWT's vaak als access tokens in:

  • web API's
  • SPA's
  • mobiele apps
  • systemen die OAuth 2.0 of OpenID Connect (OIDC) gebruiken

JWT-structuur: header, payload en signature

Een JWT is een compacte string met drie delen, elk base64url-gecodeerd en gescheiden door punten:

header.payload.signature

Voorbeeld (geredigeerd):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNzAwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c…

1) Header

De header beschrijft hoe het token is gemaakt—het belangrijkste is het ondertekeningsalgoritme (bijv. HS256, RS256/ES256) en het tokentype.

Veelvoorkomende velden:

  • typ: vaak "JWT" (meestal genegeerd in de praktijk)
  • alg: het gebruikte ondertekeningsalgoritme
  • kid: een key identifier om de verifier te helpen de juiste sleutel te kiezen tijdens rotatie

Beveiligingsopmerking: vertrouw de header niet klakkeloos. Handhaaf een allowlist van algoritmen die je daadwerkelijk gebruikt, en accepteer niet alg: "none".

2) Payload

De payload bevat “claims” (velden) over de gebruiker en de context van het token: voor wie het is, wie het heeft uitgegeven en wanneer het verloopt.

Belangrijk: JWT's zijn standaard niet versleuteld. Base64url-codering maakt het token URL-veilig; het verbergt de data niet. Iedereen die het token krijgt, kan de header en payload decoden.

Daarom moet je vermijden geheimen (wachtwoorden, API-sleutels) of gevoelige persoonlijke gegevens in een JWT te zetten.

3) Signature

De signature wordt gemaakt door de header + payload te ondertekenen met een sleutel:

  • HS256: een gedeeld geheim ondertekent en verifieert
  • RS256/ES256: een private key ondertekent; een public key verifieert

De signature biedt integriteit: de server kan verifiëren dat het token niet is aangepast en door een vertrouwde ondertekenaar is aangemaakt. Het biedt geen vertrouwelijkheid.

Grootte-overwegingen

Omdat een JWT header en payload bevat bij elke aanvraag waarin het wordt meegestuurd, betekenen grotere tokens meer bandbreedte en overhead. Houd claims compact en geef de voorkeur aan identifiers boven omvangrijke data.

Payload en claims: wat je kunt (en niet moet) opslaan

Modelleer tokenflows snel
Prototypeer access- en refresh-tokenflows zonder telkens boilerplate te herschrijven.

Claims vallen meestal in twee categorieën: registered (gestandaardiseerde namen) en custom (veldjes van je app).

Veelvoorkomende registered claims

  • iss (issuer): wie het token heeft gemaakt
  • sub (subject): over wie het token gaat (vaak een gebruikers-ID)
  • aud (audience): voor wie het token bedoeld is (bijv. een specifieke API)
  • exp (expiration time): wanneer het token niet meer geaccepteerd mag worden
  • iat (issued at): wanneer het token is aangemaakt
  • nbf (not before): het token mag niet vóór deze tijd worden geaccepteerd

Custom claims: houd ze minimaal

Neem alleen op wat de ontvangende service echt nodig heeft om een autorisatiebeslissing te nemen.

Goede voorbeelden:

  • een stabiel intern gebruikers-ID (user_id)
  • een kleine set rollen/permissions (alleen als je ze actueel kunt houden)
  • een tenant/organisatie-ID in multi-tenant apps

Vermijd “convenience claims” die veel profieldata dupliceren. Ze maken het token groot, raken snel verouderd en vergroten de impact als het token lekt.

Wat je nooit in een JWT-payload moet zetten

Omdat de payload leesbaar is, zet er niet in:

  • wachtwoorden, API-sleutels, refresh tokens of andere geheimen
  • betaalgegevens, burgerservicenummers of gevoelige persoonlijke data
  • alles wat je niet zou willen dat gekopieerd wordt vanuit een browser, proxy of log

Als je gevoelige informatie nodig hebt, sla die server-side op en zet alleen een referentie (zoals een ID) in het token—of gebruik een versleuteld tokenformaat (JWE) waar dat passend is.

Hoe de signature werkt (en wat het garandeert)

Ondertekening is geen encryptie.

  • Ondertekenen is als het dichtplakken van een brief: mensen kunnen hem lezen, maar ze kunnen verifiëren dat hij niet is veranderd.
  • Encryptie is als het op slot doen van de brief in een doos: alleen iemand met de sleutel kan hem lezen.

Wanneer een JWT wordt uitgegeven, ondertekent de server de gecodeerde header + payload. Wanneer het token later wordt gepresenteerd, recomputeert de server de signature en vergelijkt die. Als iemand één teken verandert (bijv. "role":"user" naar "role":"admin"), faalt de verificatie en wordt het token afgewezen.

JWT vs OAuth, OpenID Connect en tokentypes

JWT is een tokenformaat. OAuth 2.0 en OpenID Connect (OIDC) zijn protocollen die beschrijven hoe apps tokens opvragen, uitgeven en gebruiken.

OAuth 2.0 en access/refresh tokens

OAuth 2.0 gaat vooral over autorisatie: een app toegang geven tot een API namens een gebruiker zonder het wachtwoord te delen.

  • Access token: gepresenteerd aan een API om toestemming te bewijzen; kan een JWT of een opaque token zijn
  • Refresh token: een langerlevend token dat wordt gebruikt om nieuwe access tokens te krijgen

Access tokens zijn typisch kortlevend (minuten). Korte levensduur beperkt schade bij lekkage.

OpenID Connect (OIDC) en ID-tokens

OIDC voegt authenticatie (wie is de gebruiker) toe bovenop OAuth 2.0 en introduceert een ID token, dat meestal een JWT is.

  • ID token: voor de client app om de identiteit van de gebruiker te bevestigen
  • Access token: voor de API om aanvragen te autoriseren

Een belangrijke regel: gebruik geen ID token om een API aan te roepen.

Als je meer context wilt over praktische flows, zie de blogpost over JWT-authenticatieflows.

Veelvoorkomende JWT-authenticatieflow

Own Your Implementation
Behoud controle door de broncode te exporteren zodra je JWT-setup klaar is.

Een typische flow ziet er zo uit:

1) Inloggen

De gebruiker logt in (email/wachtwoord, SSO, enz.). Als dat lukt, maakt de server een JWT (vaak een access token) met essentiële claims zoals subject en vervaltatum.

2) Tokenuitgifte

De server ondertekent het token en retourneert het aan de client (webapp, mobiele app of een andere service).

3) API-aanroepen

Voor beschermde endpoints voegt de client de JWT toe in de Authorization header:

Authorization: Bearer \u003cJWT\u003e

4) Verificatie

Voordat de aanvraag wordt bediend, controleert de API doorgaans:

  • signature (integriteit + vertrouwde issuer)
  • exp (niet verlopen)
  • iss (verwachte issuer)
  • aud (bedoeld voor deze API)

Als alle controles slagen, beschouwt de API de gebruiker als geauthenticeerd en past autorisatieregels toe (bijv. record-level permissies).

5) Even iets over klokafwijking

Omdat systeemklokken kunnen afwijken, laten veel systemen kleine clock skew toe bij het valideren van tijdgebaseerde claims zoals exp (en soms nbf). Houd de skew klein om te voorkomen dat je de geldigheid van tokens onbedoeld verlengt.

Waar JWT's veilig op te slaan

De opslagkeuze verandert wat aanvallers kunnen stelen en hoe makkelijk ze een token kunnen hergebruiken.

Browser-apps: geheugen vs localStorage vs cookies

In-geheugen opslag (vaak aanbevolen voor SPA's) houdt het access token in JS-state. Het wordt gewist bij refresh en vermindert het risico op later stelen, maar een XSS-bug kan het nog steeds uitlezen tijdens het draaien van de pagina. Combineer dit met kortlevende access tokens en een refresh-flow.

localStorage/sessionStorage zijn makkelijk maar riskant: elke XSS kan tokens exfiltreren uit webopslag. Als je ze gebruikt, beschouw XSS-preventie als ononderhandelbaar (CSP, escaping, dependency hygiene) en houd tokens kortlevend.

Secure cookies (vaak de veiligste standaard voor web) slaan tokens op in een HttpOnly cookie zodat JavaScript ze niet kan lezen—dit vermindert de impact van XSS-diefstal. Het nadeel is CSRF-risico, omdat browsers cookies automatisch meesturen.

Als je cookies gebruikt, zet dan:

  • HttpOnly
  • Secure (alleen HTTPS)
  • SameSite=Lax of SameSite=Strict (sommige cross-site flows hebben SameSite=None; Secure nodig)

Overweeg ook CSRF-tokens voor state-changing requests.

Mobiele apps: geef voorkeur aan OS secure storage

Op iOS/Android bewaar je tokens in de platformsecure storage (Keychain / Keystore-backed storage). Vermijd platte bestanden of preferences. Als je dreigingsmodel geroot/jailbroken devices omvat, ga ervan uit dat extractie mogelijk is en vertrouw op kortlevende tokens en server-side controles.

Least privilege

Beperk wat een token kan doen: gebruik minimale scopes/claims, houd toegangstokens kortlevend en vermijd het insluiten van gevoelige data.

Veelvoorkomende JWT-beveiligingsvalkuilen om te vermijden

JWT's zijn handig, maar veel incidenten ontstaan door voorspelbare fouten. Behandel een JWT als contant geld: wie het heeft, kan het vaak uitgeven.

1) Te lange geldigheid

Als een token dagen of weken geldig is, geeft een lek een aanvaller dat hele venster.

Geef de voorkeur aan kortlevende access tokens (minuten) en vernieuw ze via een veiliger mechanisme. Als je “remember me” nodig hebt, doe dat met refresh tokens en server-side controles.

2) Issuer- en audience-checks overslaan

Geldige handtekeningen zijn niet genoeg. Controleer iss en aud, en valideer tijdclaims zoals exp en nbf.

3) Vertrouwen op gedecodeerde payloads

Decoderen is geen verificatie. Verifieer altijd de handtekening op de server en handhaaf permissies server-side.

4) Verwarring over algoritmen en sleutels

  • Accepteer niet zomaar elk algoritme dat een token claimt. Gebruik een allowlist met verwachte algoritmen.
  • Verwissel geen symmetrische sleutels (HS256) met publieke/private sleutels (RS256/ES256).
  • Beperk blast radius door sleutels per omgeving te scheiden en ze te roteren.

5) Tokenlekken via URL's, logs en referrers

Vermijd het plaatsen van JWT's in queryparameters. Ze kunnen in browsergeschiedenis, serverlogs, analytics en referrer-headers terechtkomen.

Gebruik in plaats daarvan Authorization: Bearer ....

6) Geen plan voor key-rotatie of intrekking

Ga ervan uit dat sleutels en tokens kunnen lekken. Roteer signing keys, gebruik kid om soepele rotatie te ondersteunen en heb een intrekkingsstrategie (korte vervaltijden + mogelijkheid om accounts/sessies uit te schakelen). Voor opslagadvies, zie de blogpost over waar JWT's veilig te bewaren.

Wanneer JWT gebruiken (en wanneer niet)

Scaffold een Go JWT API
Genereer een Go API met JWT-middleware, claim-controles en nette request-afhandeling.

JWT's zijn nuttig, maar ze zijn niet automatisch de beste keuze. De echte vraag is of je profiteert van een self-contained token dat geverifieerd kan worden zonder bij elke aanvraag een database-opvraging.

Goede toepassingen voor JWT

  • Stateless API's op schaal: lokale verificatie (handtekening + expiry) zonder per-request sessie-opvragingen
  • Meerdere services / microservices: gedeelde verificatieregels en publieke sleutels
  • SPA's en mobiele apps: clients die direct API's aanroepen
  • Kortlevende access tokens: beperkte impact bij diefstal

Wanneer JWT een slechte keuze is

  • Onmiddellijke intrekking is vereist: sessies zijn eenvoudiger als je overal meteen wilt uitloggen zonder extra infrastructuur
  • Je moet gevoelige data meegeven: typische JWT's zijn ondertekend, niet versleuteld
  • Langlevende tokens: ze hebben hoge waarde en zijn het waard om te stelen

Wanneer eenvoudige sessiecookies beter zijn

Voor traditionele server-gerenderde webapps waar eenvoudige invalidatie belangrijk is, zijn server-side sessies met HttpOnly cookies vaak de simpelere, veiligere standaard.

Snelle beslis-checklist

Kies JWT als je stateless verificatie tussen services nodig hebt en je tokens kortlevend kunt houden.

Vermijd JWT als je onmiddellijke intrekking nodig hebt, je van plan bent gevoelige data in het token te bewaren, of je sessiecookies zonder frictie kunt gebruiken.

Praktische checklist en veelgestelde vragen

Verificatie-checklist (wat je elke keer moet controleren)

  1. Handtekening is geldig

Verifieer met de juiste sleutel en verwacht algoritme. Weiger ongeldige handtekeningen—geen uitzonderingen.

  1. exp (expiration)

Zorg dat het token niet is verlopen.

  1. nbf (not before)

Als aanwezig, zorg dat het token niet te vroeg wordt gebruikt.

  1. aud (audience)

Bevestig dat het token voor jouw API/service bedoeld is.

  1. iss (issuer)

Bevestig dat het token van de verwachte issuer komt.

  1. Sanity checks (aanbevolen)

Valideer token-formaat, handhaaf maximale grootte en weiger onverwachte claim-types om edge-case bugs te verminderen.

HS256 vs RS256/ES256 kiezen

  • HS256 (symmetrische sleutel): één gedeeld geheim ondertekent en verifieert.

    • Geschikt voor: één app/API beheerd door één team.
    • Let op: elke verifier met het geheim kan ook tokens minten.
  • RS256 / ES256 (asymmetrische sleutels): private key ondertekent; public key verifieert.

    • Geschikt voor: meerdere services die tokens verifiëren; publieke sleutels verspreiden zonder signeren mogelijk te maken.
    • Operationele noot: rotatie is vaak veiliger omdat alleen de signer de private key heeft.

Vuistregel: als meer dan één onafhankelijk systeem tokens moet verifiëren (of je vertrouwt niet elke verifier volledig), geef dan de voorkeur aan RS256/ES256.

Monitoring en logging (zonder tokens te lekken)

  • Log geen ruwe tokens (headers, cookies, query strings).
  • Als je correlatie nodig hebt, log een token-fingerprint (bijv. een hash) of veilige metadata (iss, aud en een gebruikers-ID alleen als beleid dat toestaat).
  • Let op anomalieën: handtekening-fouten, pieken in verlopen tokens, ongebruikelijke audiences/issuers en verdachte refresh-patronen.

FAQs

Is JWT versleuteld?

Niet standaard. De meeste JWT's zijn ondertekend, niet versleuteld, wat betekent dat de inhoud door iedereen gelezen kan worden die het token heeft. Gebruik JWE of laat gevoelige data buiten JWT's.

Kan ik een JWT intrekken?

Niet eenvoudig als je alleen op self-contained access tokens vertrouwt. Gebruikelijke benaderingen zijn kortelevende access tokens, deny-lists voor hoge-risico gebeurtenissen, of refresh tokens met rotatie.

Hoe lang moet exp zijn?

Zo kort als je UX en architectuur toelaten. Veel API's gebruiken minuten voor access tokens, gecombineerd met refresh tokens voor langere sessies.

JWT-beschermde apps sneller bouwen met Koder.ai

Als je JWT-auth implementeert in een nieuwe API of SPA, is veel werk repetitief: middleware aansluiten, iss/aud/exp valideren, cookieflags instellen en tokenafhandeling uit logs houden.

Met Koder.ai kun je snel een webapp (React), backendservices (Go + PostgreSQL) of een Flutter mobiele app genereren via een chatgestuurde workflow—dan itereren in een planningsmodus, snapshots en rollback gebruiken terwijl je beveiliging verfijnt, en de broncode exporteren wanneer je klaar bent. Het is een praktische manier om JWT-authflows te versnellen en toch controle te houden over verificatielogica, sleutelrotatie en deployment/hosting-instellingen (inclusief custom domains).

Veelgestelde vragen

What is a JWT, and where do I usually send it?

Een JWT (JSON Web Token) is een compacte, URL-veilige string die claims (gegevensvelden) bevat en door een server geverifieerd kan worden. Het wordt vaak bij API-aanvragen meegestuurd via:

  • Authorization: Bearer \u003ctoken\u003e

Het kernidee: de server kan de integriteit van het token valideren (via de handtekening) zonder voor elke aanvraag een per-gebruiker sessieregister te hoeven raadplegen.

How is JWT authentication different from server sessions?

Sessie-authenticatie slaat doorgaans state op de server (een sessierecord gekoppeld aan een cookie/session ID). Bij JWT-gebaseerde auth presenteert de client bij elke aanvraag een ondertekend token dat de API valideert.

JWT's zijn populair voor API's en multi-service-architecturen omdat verificatie lokaal kan gebeuren, wat de behoefte aan gedeelde sessieopslag vermindert.

“Stateless” omvat nog steeds vaak server-side controles zoals deny-lists, controle van gebruikersstatus of key-rotatie.

What are the three parts of a JWT (header, payload, signature)?

Een JWT bestaat uit drie Base64URL-gecodeerde onderdelen, gescheiden door punten:

  • header.payload.signature

De header beschrijft hoe het token is ondertekend, de payload bevat claims (zoals sub, exp, aud) en de signature laat de server detecteren of er is geknoeid.

Is a JWT encrypted, and can people read what’s inside?

Nee. Standaard JWT's zijn meestal ondertekend, niet versleuteld.

  • Ondertekenen bewijst integriteit (het is niet gewijzigd) en authenticiteit (uitgegeven door een vertrouwde partij).
  • Iedereen met het token kan de header en payload Base64URL-decoden en lezen.

Als je vertrouwelijkheid nodig hebt, overweeg dan JWE (versleutelde tokens) of bewaar gevoelige data server-side en zet alleen een verwijzing in het JWT.

What does the JWT signature guarantee—and what doesn’t it guarantee?

De handtekening laat de server verifiëren dat het token niet is aangepast en is uitgegeven door iemand met de juiste sleutel.

Het garandeert niet:

  • dat de payload verborgen is
  • dat de gebruiker nog steeds actief is (tenzij je dat controleert)
  • dat een token vóór exp automatisch ingetrokken is

Behandel een token als een credential: als het lekt, kan het doorgaans worden hergebruikt totdat het verloopt.

What are `alg` and `kid` in the JWT header, and why do they matter?

alg vertelt welke algoritme is gebruikt (bijv. HS256 vs RS256). kid is een key identifier die helpt om tijdens key-rotatie de juiste verificatiesleutel te kiezen.

Veiligheidsregels:

  • Gebruik een allowlist met verwachte algoritmen; accepteer niet willekeurige alg-waarden.
  • Accepteer nooit alg: "none".
  • Laat een onbetrouwbare kid-waarde het sleutelzoekgedrag niet onveilig maken.
Which JWT claims should I include in the payload?

Begin met gestandaardiseerde registered claims en houd custom claims minimaal.

Veelvoorkomende registered claims:

  • iss (issuer)
  • sub (subject / gebruikers-ID)
  • aud (audience / bedoelde API)
  • exp (expiration)
  • iat (issued at)
  • nbf (not before)

Vermijd het plaatsen van geheimen of gevoelige persoonsgegevens in de payload, omdat die leesbaar zijn als het token wordt gelekt.

How do JWT, OAuth 2.0, and OpenID Connect relate (access tokens vs ID tokens)?

JWT is een tokenformaat; OAuth 2.0 en OpenID Connect zijn protocollen.

Typische mapping:

  • Access token: gebruikt om een API aan te roepen (kan een JWT of een opaque token zijn).
  • ID token (OIDC): gebruikt door de client-app om identiteit te bevestigen (meestal een JWT).
  • Refresh token: gebruikt om nieuwe access tokens te verkrijgen (vaak opaque; zeer gevoelig).

Belangrijk: gebruik geen ID token om een API aan te roepen alleen omdat het “eruitziet” als een JWT-access token.

Where should I store JWTs safely in a browser app?

Voor browser-apps zijn de gebruikelijke opties:

  • In geheugen: vermindert het risico dat het later wordt gestolen, maar bij een actieve XSS kan het nog steeds gelezen worden.
  • localStorage/sessionStorage: handig, maar elke XSS kan tokens exfiltreren.
  • HttpOnly Secure cookies: vaak het veiligst voor web omdat JS ze niet kan lezen, maar dit vereist CSRF-bescherming (bijv. SameSite + CSRF-tokens voor state-changing requests).

Wat je ook kiest: maak toegangstokens kortlevend en minimaliseer token-privileges.

What checks should my API perform when validating a JWT?

Minimaal moet je valideren:

  • handtekening (met de juiste sleutel en allowlisted algoritme)
  • exp (niet verlopen)
  • iss (verwachte issuer)
  • aud (bedoeld voor jouw API)
  • nbf (als aanwezig)

Voeg ook praktische guardrails toe:

  • handhaaf maximale token-grootte
  • weiger onverwachte claim-types
  • sta kleine clock skew toe om tijdsafwijkingen op te vangen

Related posts