JWT's ondertekenen en verifiëren met python-jose
Codeer en decodeer JWT's met claims, vervaldatum en audiencevalidatie en bescherm routes tegen manipulatie.
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,expenaud. - 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
JWTErrorop om het verzoek af te wijzen met401 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
ExpiredSignatureErrorop en daarna een algemeneJWTError. - jose past standaard een kleine speling toe voor klokafwijkingen; je kunt die met
optionsaanpassen.
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 tokenaudbevat, 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_tokenbouwt de claims op en ondertekent ze.verify_tokendecodeert 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
JWTErrorom 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"); houdsubals 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
expveroorzaakt automatischExpiredSignatureError; handel deze fout af om vernieuwing te starten. - Doelgroep via
audmoet bij het decoderen overeenkomen metaudience=, anders krijg jeJWTClaimsError. - Verifieer in FastAPI binnen een
Depends(get_current_user)-afhankelijkheid en zet fouten om inHTTPException(401).
Hierna: vernieuwingstokens en roterende ondertekeningssleutels.
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
- OAuth2 Password Flow en tokenuitgifte
- JWT's ondertekenen en verifiëren met python-jose
- Refresh tokens en tokenrotatie
- Autorisatie op basis van scopes en roleguards