Bei 401 Unauthorized mit einem JWT gehe ich die Authentifizierungskette von außen nach innen durch. Ich beginne beim HTTP-Request, dann Zeitfenster, Signatur, Issuer, Audience und Token-Typ. Erst wenn diese Schichten stimmen, beschäftige ich mich mit Scopes und Rollen.
Wenn Expected Issuer und Audience bekannt sind, starte ich mit dem JWT Debugger.
1. Authorization-Header prüfen, bevor ich Claims analysiere
Ein korrektes Bearer-Token sieht im Request so aus:
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Ich prüfe:
- Header fehlt komplett;
Bearerfehlt oder ist falsch geschrieben;- Token enthält Anführungszeichen;
- Proxy/Gateway entfernt
Authorization; - Client sendet altes Token aus Cache;
- ID Token statt Access Token wird gesendet.
Ein perfektes JWT hilft nicht, wenn es die API nie erreicht.
2. WWW-Authenticate und Server-Logs auswerten
RFC 6750 definiert den WWW-Authenticate-Header für Bearer-Fehler. Wenn verfügbar, lese ich ihn zusammen mit Server-Logs:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token"
invalid_token kann mehrere Ursachen haben. Ich suche nach präziseren Meldungen wie:
token expired
invalid signature
invalid issuer
invalid audience
kid not found
token not active
Clientseitige Fehlermeldungen allein sind oft zu grob.
3. Zeitclaims exp, nbf, iat prüfen
Mit dem JWT Expiry Checker prüfe ich, ob das Token abgelaufen oder noch nicht aktiv ist.
now < nbf => noch nicht aktiv
now >= exp => abgelaufen
Ich achte auf Sekunden/Millisekunden und auf die Uhr des Verifiers. Bei Zeitfehlern hilft die exp-Anleitung.
4. Signatur und Algorithmus als eigene Schicht behandeln
Ein Token kann perfekt lesbar und trotzdem nicht authentisch sein. Ich prüfe:
- erlaubter
alg; - vertrauenswürdiger Schlüssel oder JWKS;
kid-Auswahl;- Key Rotation;
- PEM/Secret-Encoding;
- unveränderte
header.payload-Bytes.
Wenn ich Key-Material habe, nutze ich den JWT Signature Verifier oder bei JWKS den JWKS Validator.
5. Issuer exakt mit der Vertrauenskonfiguration vergleichen
Ich vergleiche iss nicht mit „ähnlich aussehenden“ URLs. Unterschiedliche Tenant-, Realm- oder Environment-Identifier können unterschiedliche Vertrauensdomänen darstellen.
expected: https://login.example.com/prod
actual: https://login.example.com/dev
Wenn iss falsch ist, prüfe ich gleichzeitig die verwendete JWKS-/Discovery-Quelle.
6. Audience als Empfängerprüfung behandeln
Die Signatur kann gültig und der Issuer korrekt sein, während aud für eine andere API bestimmt ist.
{ "aud": ["api://billing", "api://reporting"] }
Erwartet die Orders-API api://orders, ist die Ablehnung korrekt. Der Audience-Mismatch-Guide deckt String/Array, ID-Token-Verwechslung und falsche Resources ab.
7. Access Token und ID Token unterscheiden
Ein häufiges Muster: Frontend erhält Access Token und ID Token, aber die falsche Variable landet im API-Header. Hinweise sind eine aud, die wie die Client-ID aussieht, OIDC-spezifische Claims und fehlende API-Scopes.
Ich löse das im Client-Flow und nicht dadurch, dass die API das falsche Token akzeptiert.
8. 401 und 403 auseinanderhalten
Für meine Diagnose verwende ich diese praktische Trennung:
| Status | Typische Bedeutung | Prüfschwerpunkt |
|---|---|---|
| 401 | Authentifizierung/Token nicht akzeptiert | Header, Signatur, Zeit, iss, aud |
| 403 | Identität akzeptiert, Berechtigung fehlt | Scope, Rolle, Policy |
Frameworks und APIs können Details anders abbilden, aber die Trennung hilft mir, nicht sofort Berechtigungen zu debuggen, wenn die Authentifizierung noch scheitert.
9. Proxy- und Gateway-Probleme nicht vergessen
In mehrstufigen Setups kann das JWT korrekt sein und trotzdem 401 entstehen, weil:
- Reverse Proxy
Authorizationnicht weiterleitet; - Gateway gegen einen anderen Issuer validiert;
- Edge Cache alte JWKS hält;
- unterschiedliche Services unterschiedliche Audience-Werte erwarten;
- Middleware in anderer Reihenfolge läuft als gedacht.
Ich vergleiche daher Request-Logs am Eingang und unmittelbar vor der Anwendung.
Meine 401-Entscheidungskette
Authorization Header vorhanden?
└─ nein -> Client/Proxy reparieren
└─ ja
↓
Zeitfenster gültig?
└─ nein -> Refresh/Reauthentication/Clock prüfen
└─ ja
↓
Signatur mit vertrauenswürdigem Key gültig?
└─ nein -> alg/kid/key/JWKS debuggen
└─ ja
↓
iss korrekt?
└─ nein -> Issuer/Tenant/Environment korrigieren
└─ ja
↓
aud enthält erwartete API?
└─ nein -> Resource/Token Acquisition korrigieren
└─ ja
↓
Token-Typ und Berechtigungen prüfen
Mein JWT-401-Workflow
- Ich reproduziere den Request und sichere Status, Response Header und relevante Server-Logs.
- Ich bestätige
Authorization: Bearer <token>am API-Eingang. - Ich dekodiere das Originaltoken mit dem JWT Decoder.
- Ich prüfe
exp,nbfund Serverzeit. - Ich prüfe erlaubten Algorithmus und Signatur.
- Ich vergleiche
issexakt mit dem erwarteten Issuer. - Ich vergleiche
audmit der erwarteten API-Audience. - Ich bestätige, dass ein Access Token und nicht ein ID Token gesendet wird.
- Erst danach prüfe ich Scope, Rolle und anwendungsspezifische Authorization.
- Ich korrigiere die fehlerhafte Schicht statt eine Validierungsregel abzuschalten.
So wird ein 401 von einem unspezifischen Fehlercode zu einer geordneten Kette überprüfbarer Hypothesen.