Bootcamp backendontwikkeling met FastAPI · Les

JWT's ondertekenen en verifiëren met python-jose

Codeer en decodeer JWT's met claims, vervaldatum en audiencevalidatie en bescherm routes tegen manipulatie.

Les 2 van 413 stappen

JWT's ondertekenen en verifiëren met python-jose is een gratis Bootcamp backendontwikkeling met FastAPI-les op CoddyKit. Dit is les 2 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Bootcamp backendontwikkeling met FastAPI. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Bootcamp backendontwikkeling met FastAPI bevat in totaal 4 lessen.

Waarom JWT's voor stateless authenticatie

Een JWT (JSON Web Token) is een compact, ondertekend token dat claims over een gebruiker bevat. Zodra je FastAPI-app bij het inloggen een JWT uitgeeft, stuurt de client het bij elk verzoek terug en controleer je het zonder een sessieopslagplaats te gebruiken.

  • Stateless — de server slaat geen sessies op; de handtekening bewijst de authenticiteit.
  • Wijzigingsbestendig aantoonbaar — elke wijziging in de payload maakt de handtekening ongeldig.
  • Draagbaar — hetzelfde token werkt in services die het geheim of de publieke sleutel delen.

In deze les gebruiken we python-jose om tokens met claims, vervaltijd en doelgroepvalidatie te coderen (ondertekenen) en te decoderen (controleren).

Anatomie van een JWT

Een JWT heeft drie met punten verbonden Base64URL-onderdelen: header.payload.signature.

  • Header — algoritme en tokentype, bijvoorbeeld {"alg": "HS256", "typ": "JWT"}.
  • Payload — de claims (gegevens), zoals sub, exp en aud.
  • Signature — een sleutelgebonden hash van header + payload, die bewijst dat het token niet is gewijzigd.

De standaardclaims (geregistreerde claims) die je het vaakst gebruikt zijn: sub (onderwerp/gebruikers-id), exp (vervaltijdstip), iat (uitgegeven op), aud (doelgroep) en iss (uitgever). De payload is alleen gecodeerd, niet versleuteld. Zet er dus nooit geheimen in, zoals wachtwoorden.

python-jose installeren en importeren

Installeer python-jose met de cryptografische backend, zodat RSA- en EC-algoritmen ook werken:

  • pip install "python-jose[cryptography]"

De twee functies die je voortdurend gebruikt, staan in jose.jwt: jwt.encode(...) om te ondertekenen en jwt.decode(...) om te verifiëren. Bij fouten worden subklassen van JWTError opgegooid, zodat je alle tokenproblemen netjes kunt afhandelen.

from jose import jwt
from jose.exceptions import JWTError, ExpiredSignatureError, JWTClaimsError

print("encode:", callable(jwt.encode))
print("decode:", callable(jwt.decode))
print("base error:", issubclass(ExpiredSignatureError, JWTError))

Je eerste token coderen

Om een token te ondertekenen geef je een claims-dict, een geheime sleutel en een algoritme door. Voor symmetrische ondertekening gebruiken we HS256, waarbij hetzelfde geheim wordt gebruikt voor ondertekenen en verifiëren.

  • Zet de gebruikers-id in sub — deze moet een tekenreeks zijn.
  • Houd het geheim lang en willekeurig; laad het in echte apps uit een omgevingsvariabele.

Het resultaat is één URL-veilige tekenreeks die je aan de client kunt teruggeven.

from jose import jwt

SECRET = "a-very-long-random-secret-string-change-me"
ALGO = "HS256"

claims = {"sub": "user-42", "role": "admin"}
token = jwt.encode(claims, SECRET, algorithm=ALGO)
print(token[:40] + "...")
print("dot count:", token.count("."))

Decoderen en verifiëren

jwt.decode doet twee dingen tegelijk: de handtekening controleren en de claims teruggeven. Als de handtekening onjuist is, wordt een JWTError opgegooid in plaats van gegevens teruggegeven.

  • Geef via algorithms=[...] hetzelfde algoritme of dezelfde algoritmen door waarmee je hebt ondertekend — vertrouw nooit alleen op het algoritme dat in de tokenheader staat.
  • Een geslaagde decode betekent dat het token authentiek en ongemanipuleerd is.

Het onderstaande voorbeeld ondertekent een token, verifieert het daarna en leest de claims eruit.

from jose import jwt

SECRET = "a-very-long-random-secret-string-change-me"

token = jwt.encode({"sub": "user-42", "role": "admin"}, SECRET, algorithm="HS256")
payload = jwt.decode(token, SECRET, algorithms=["HS256"])
print("sub:", payload["sub"])
print("role:", payload["role"])

Manipulatie detecteren

Daar draait ondertekenen volledig om. Als een aanvaller één teken in de payload verandert, mislukt de verificatie omdat de handtekening niet meer overeenkomt.

  • Vang JWTError op om het verzoek af te wijzen met 401 Unauthorized.
  • Decodeer in productie nooit met verify_signature=False — daarmee sla je de beveiligingscontrole volledig over.

Het fragment maakt een token ongeldig en laat zien dat de verificatie een fout oplevert.

from jose import jwt
from jose.exceptions import JWTError

SECRET = "a-very-long-random-secret-string-change-me"
token = jwt.encode({"sub": "user-42"}, SECRET, algorithm="HS256")

# Tamper: change the last character of the token
tampered = token[:-1] + ("A" if token[-1] != "A" else "B")
try:
    jwt.decode(tampered, SECRET, algorithms=["HS256"])
    print("accepted (BAD)")
except JWTError as e:
    print("rejected tampered token:", type(e).__name__)

Vervaltijd toevoegen met exp

Tokens moeten een korte levensduur hebben. De claim exp is een Unix-tijdstempel (seconden sinds het tijdperk, in UTC). python-jose wijst verlopen tokens automatisch af tijdens het decoderen en gooit dan ExpiredSignatureError op.

  • Bereken de vervaltijd met UTC die een tijdzone bevat: datetime.now(timezone.utc) + timedelta(...).
  • Toegangstokens zijn meestal 15-30 minuten geldig; vernieuwingstokens blijven langer geldig.

Je kunt voor exp een datetime of een geheel getal doorgeven — jose zet datums automatisch voor je om in tijdstempels.

from datetime import datetime, timedelta, timezone
from jose import jwt

SECRET = "a-very-long-random-secret-string-change-me"
expire = datetime.now(timezone.utc) + timedelta(minutes=30)
claims = {"sub": "user-42", "exp": expire}

token = jwt.encode(claims, SECRET, algorithm="HS256")
payload = jwt.decode(token, SECRET, algorithms=["HS256"])
print("exp claim (unix):", payload["exp"])
print("valid for ~30 min")

Verlopen tokens afhandelen

Als exp van een token in het verleden ligt, gooit jwt.decode ExpiredSignatureError op (een subklasse van JWTError). Handel deze fout afzonderlijk af, zodat je de client kunt vragen om het token te vernieuwen in plaats van opnieuw in te loggen.

  • Vang eerst ExpiredSignatureError op en daarna een algemene JWTError.
  • jose past standaard een kleine speling toe voor klokafwijkingen; je kunt die met options aanpassen.

Hier geven we een al verlopen token uit om te bewijzen dat de controle wordt uitgevoerd.

from datetime import datetime, timedelta, timezone
from jose import jwt
from jose.exceptions import ExpiredSignatureError, JWTError

SECRET = "a-very-long-random-secret-string-change-me"
past = datetime.now(timezone.utc) - timedelta(minutes=5)
token = jwt.encode({"sub": "user-42", "exp": past}, SECRET, algorithm="HS256")

try:
    jwt.decode(token, SECRET, algorithms=["HS256"])
except ExpiredSignatureError:
    print("token expired -> ask client to refresh")
except JWTError:
    print("other token error")

Doelgroep valideren met aud

De claim aud (doelgroep) geeft aan voor wie het token bestemd is — bijvoorbeeld voor je API. Als je deze claim bij het coderen instelt, moet je bij het decoderen de overeenkomstige audience= doorgeven; anders gooit jose JWTClaimsError op.

  • Voorkomt dat een token dat voor de ene service is uitgegeven opnieuw tegen een andere service wordt gebruikt.
  • Als je audience= weglaat terwijl het token aud bevat, mislukt de validatie — geef de waarde expliciet door.

Het voorbeeld ondertekent een token met een doelgroep en valideert die bij het decoderen.

from jose import jwt
from jose.exceptions import JWTClaimsError

SECRET = "a-very-long-random-secret-string-change-me"
token = jwt.encode(
    {"sub": "user-42", "aud": "fastapi-bootcamp-api"},
    SECRET, algorithm="HS256",
)

payload = jwt.decode(token, SECRET, algorithms=["HS256"], audience="fastapi-bootcamp-api")
print("aud ok:", payload["aud"])

try:
    jwt.decode(token, SECRET, algorithms=["HS256"], audience="some-other-api")
except JWTClaimsError as e:
    print("wrong audience rejected:", type(e).__name__)

Een herbruikbare tokenhelper

In een echt bootcamp-project wikkel je het ondertekenen en verifiëren in kleine helpers, zodat routes overzichtelijk blijven. Bundel de standaardclaims — sub, exp, iat, aud en iss — op één plek.

  • create_access_token bouwt de claims op en ondertekent ze.
  • verify_token decodeert met alle validaties en geeft de payload terug of gooit een fout op.

Deze module in pure Python importeert niets uit FastAPI, dus je kunt hem eenvoudig afzonderlijk met unittests testen.

from datetime import datetime, timedelta, timezone
from jose import jwt
from jose.exceptions import JWTError

SECRET = "a-very-long-random-secret-string-change-me"
ALGO, AUD, ISS = "HS256", "fastapi-bootcamp-api", "auth-service"

def create_access_token(sub, minutes=30):
    now = datetime.now(timezone.utc)
    claims = {"sub": sub, "iat": now, "exp": now + timedelta(minutes=minutes),
              "aud": AUD, "iss": ISS}
    return jwt.encode(claims, SECRET, algorithm=ALGO)

def verify_token(token):
    return jwt.decode(token, SECRET, algorithms=[ALGO], audience=AUD, issuer=ISS)

t = create_access_token("user-42")
print("verified sub:", verify_token(t)["sub"])

Een FastAPI-route beveiligen

In FastAPI koppel je de verificatie aan een afhankelijkheid. OAuth2PasswordBearer haalt het token uit de header Authorization: Bearer .... Daarna verifieert je afhankelijkheid het token en geeft deze de huidige gebruiker terug — of gooit HTTPException(401) op.

  • Elke route die Depends(get_current_user) declareert, is nu beveiligd.
  • Zet JWTError om in een correcte 401, zodat gemanipuleerde of verlopen tokens met de juiste status worden afgewezen.

Dit is frameworkcode en wordt dus uitgevoerd in een server, niet in een zelfstandig beoordelingsprogramma.

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import jwt
from jose.exceptions import JWTError

app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
SECRET, ALGO, AUD = "change-me", "HS256", "fastapi-bootcamp-api"

def get_current_user(token: str = Depends(oauth2_scheme)):
    creds_exc = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET, algorithms=[ALGO], audience=AUD)
    except JWTError:
        raise creds_exc
    user_id = payload.get("sub")
    if user_id is None:
        raise creds_exc
    return user_id

@app.get("/me")
def read_me(user_id: str = Depends(get_current_user)):
    return {"user_id": user_id}

Snelle controle: doelgroep valideren

Je ondertekent tokens met aud="fastapi-bootcamp-api". De decode-aanroep van een teamgenoot gooit soms JWTClaimsError, zelfs bij pas uitgegeven, ongemanipuleerde tokens. Wat is de meest waarschijnlijke oorzaak?

Samenvatting: JWT's ondertekenen en verifiëren

Je kunt nu JWT's van begin tot eind uitgeven en valideren met python-jose:

  • Codeer claims met jwt.encode(claims, secret, algorithm="HS256"); houd sub als tekenreeks en bewaar het geheim in een omgevingsvariabele.
  • Decodeer met jwt.decode(token, secret, algorithms=[...]) en leg altijd de lijst met toegestane algoritmen vast.
  • Manipulatie maakt de handtekening ongeldig en veroorzaakt JWTError — wijs het verzoek af met 401.
  • Vervaltijd via exp veroorzaakt automatisch ExpiredSignatureError; handel deze fout af om vernieuwing te starten.
  • Doelgroep via aud moet bij het decoderen overeenkomen met audience=, anders krijg je JWTClaimsError.
  • Verifieer in FastAPI binnen een Depends(get_current_user)-afhankelijkheid en zet fouten om in HTTPException(401).

Hierna: vernieuwingstokens en roterende ondertekeningssleutels.

Gratis beginnen

Leer Bootcamp backendontwikkeling met FastAPI met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
21
Lessen
84

Veelgestelde vragen

Is de les “JWT's ondertekenen en verifiëren met python-jose” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad Bootcamp backendontwikkeling met FastAPI, waaronder “JWT's ondertekenen en verifiëren met python-jose”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus Bootcamp backendontwikkeling met FastAPI bevat in totaal 4 lessen.

Wat leer ik in “JWT's ondertekenen en verifiëren met python-jose”?

Codeer en decodeer JWT's met claims, vervaldatum en audiencevalidatie en bescherm routes tegen manipulatie. Je oefent met Bootcamp backendontwikkeling met FastAPI door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Bootcamp backendontwikkeling met FastAPI te beginnen?

Ervaring vooraf is niet nodig. Bootcamp backendontwikkeling met FastAPI op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.

Hoe lang duurt de les “JWT's ondertekenen en verifiëren met python-jose”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Bootcamp backendontwikkeling met FastAPI?

Ja. Elke les over Bootcamp backendontwikkeling met FastAPI bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. OAuth2 Password Flow en tokenuitgifte
  2. JWT's ondertekenen en verifiëren met python-jose
  3. Refresh tokens en tokenrotatie
  4. Autorisatie op basis van scopes en roleguards
← Terug naar Bootcamp backendontwikkeling met FastAPI