Einen Ablauf auswählen und Schritt für Schritt durchgehen: Wer schickt was an wen, was steht in der Anfrage, was kommt zurück und was bedeutet jeder einzelne Parameter. Die Werte sind dabei nicht ausgedacht – die PKCE-Challenge ist der echte SHA-256 des Verifiers, das ID-Token ist in deinem Browser signiert. Den eigenen Provider kannst du über sein Discovery-Dokument einlesen.
Ablauf wählen
Beteiligte
Diese Angaben werden in alle Anfragen unten eingesetzt. Es wird nichts gesendet – der Provider ist nachgestellt, die Werte sind echt gerechnet.
Die Werte dieses Durchlaufs
state
nonce
code_verifier
code_challenge
Frisch erzeugt, jedes Mal anders. Die Challenge ist der tatsächliche SHA-256 des Verifiers – zum Nachrechnen und Weiterverwenden.
Wer redet mit wem
Front-Channel – läuft durch den Browser, ist sichtbar
Back-Channel – direkt von Server zu Server
passiert innerhalb eines Beteiligten
1 von 1
lokalAnwendung
Der Client würfelt seine Einmalwerte
Bevor überhaupt etwas über die Leitung geht, erzeugt die Anwendung drei Zufallswerte. Der code_verifier bleibt geheim im Client; nur sein SHA-256-Abdruck (die code_challenge) geht mit auf die Reise. Damit ist ein abgefangener Autorisierungscode für einen Angreifer wertlos: Ohne den Verifier kann er ihn nicht einlösen. Die Werte hier sind echt gerechnet – die Challenge ist der tatsächliche Base64url-kodierte SHA-256 des Verifiers darüber.
Feld
Wert in diesem Durchlauf
Bedeutung
stateempfohlen
Zufallswert gegen CSRF. Der Client legt ihn in seiner Session ab und vergleicht ihn beim Rücksprung. Kommt eine Antwort mit fremdem oder fehlendem state, wird sie verworfen.
nonceempfohlen
Zufallswert gegen Token-Wiedereinspielung. Er landet als Claim im ID-Token; der Client prüft, ob derselbe Wert zurückkommt, den er losgeschickt hat.
code_verifierPflicht
43 bis 128 Zeichen aus dem URL-sicheren Alphabet. Verlässt den Client erst im Token-Request – über den Back-Channel, nicht über den Browser.
code_challengePflicht
BASE64URL(SHA-256(code_verifier)). Geht offen über den Browser, ist aber nicht zurückrechenbar.
code_challenge_methodPflicht
S256
Immer S256. Der Wert plain schickt den Verifier im Klartext und ist nur für Geräte gedacht, die kein SHA-256 können – praktisch also nie.
Front-ChannelAnwendung → Provider (IdP)GET
Weiterleitung zum Autorisierungsserver
Die Anwendung schickt den Browser des Nutzers zum Autorisierungsendpunkt – als ganz normale Weiterleitung. Alles, was hier steht, ist für den Nutzer sichtbar und steht im Browserverlauf: Diese Strecke ist der Front-Channel. Geheimnisse haben hier nichts verloren, deshalb geht auch nur die Challenge mit und nicht der Verifier.
code sagt: Ich möchte einen Autorisierungscode, keine Tokens im Browser.
client_idPflicht
Die öffentliche Kennung der Anwendung beim Provider. Kein Geheimnis.
redirect_uriPflicht
Wohin der Provider den Browser zurückschickt. Muss registriert sein.
scopePflicht
Was angefragt wird. openid macht aus OAuth 2.0 erst OpenID Connect – ohne diesen Wert gibt es kein ID-Token.
stateempfohlen
Der eben erzeugte CSRF-Wert, kommt unverändert zurück.
nonceempfohlen
Der eben erzeugte Wert, taucht im ID-Token wieder auf.
code_challengePflicht
Der SHA-256-Abdruck des Verifiers.
code_challenge_methodPflicht
S256
S256 – siehe vorheriger Schritt.
promptoptional
login | consent | none | select_account
Steuert die Anmeldemaske: login erzwingt eine neue Anmeldung, consent die erneute Zustimmung, none verbietet jede Rückfrage (für stille Erneuerung im Hintergrund) und liefert sonst den Fehler login_required.
max_ageoptional
3600
Wie alt die Anmeldung höchstens sein darf, in Sekunden. Ist die Session älter, muss der Nutzer sich erneut anmelden; das Ergebnis steht als auth_time im ID-Token.
login_hintoptional
erika@example.com
Vorbelegung des Benutzernamens, spart dem Nutzer einen Tippvorgang.
ui_localesoptional
de-DE
Wunschsprache der Anmeldemaske, als Liste nach Vorliebe sortiert.
Der redirect_uri muss beim Provider vorab registriert sein und exakt übereinstimmen – Zeichen für Zeichen, inklusive Schrägstrich am Ende. Das ist kein Schikane, sondern die einzige Bremse dagegen, dass jemand den Code auf seinen eigenen Server umleitet.
Front-ChannelNutzer / Browser → Provider (IdP)
Der Nutzer meldet sich an – beim Provider, nicht bei der Anwendung
Jetzt passiert der Teil, von dem die Anwendung nichts mitbekommt und nichts mitbekommen soll: Passwort, zweiter Faktor, Passkey, Zustimmungsdialog. Der Client sieht davon nichts – das ist der eigentliche Gewinn des Verfahrens. Besteht beim Provider schon eine Sitzung, läuft dieser Schritt unsichtbar durch, und genau das ist Single Sign-on.
Weil der Provider seine eigene Sitzung in einem Cookie hält, hängt die stille Erneuerung im Hintergrund (prompt=none im versteckten iframe) an Drittanbieter-Cookies – die Browser schalten sie gerade ab. Der vorgesehene Ersatz sind Refresh Tokens mit Rotation.
Front-ChannelProvider (IdP) → Anwendung302
Rücksprung mit dem Autorisierungscode
Der Provider schickt den Browser zurück zur registrierten Adresse und hängt den Code an. Der Code ist kurzlebig (meist eine Minute) und genau einmal einlösbar; er selbst ist noch kein Zugang zu irgendetwas. Er steht in der Adressleiste, im Verlauf und womöglich im Server-Log – genau deshalb ist er nur die halbe Miete und braucht im nächsten Schritt den Verifier dazu.
HTTP/1.1 302 Found
Location: ?code=
&state=
&iss=
Feld
Wert in diesem Durchlauf
Bedeutung
codePflicht
Der Autorisierungscode. Einmal einlösbar, kurze Lebensdauer.
stateempfohlen
Muss bitgleich dem Wert aus Schritt 1 entsprechen.
issoptional
Der Aussteller, seit RFC 9207. Schützt Anwendungen mit mehreren Providern davor, einen Code beim falschen Server einzulösen – dem Mix-Up-Angriff.
Hier wird der state geprüft, und zwar bevor irgendetwas anderes passiert. Fehlt er oder passt er nicht zur Sitzung, bricht der Client ab. Der Vergleich sollte zeitkonstant sein, und der gespeicherte Wert danach gelöscht – sonst ist er wiederverwendbar.
Back-ChannelAnwendung → Provider (IdP)POST
Code gegen Tokens tauschen
Dieser Aufruf geht direkt vom Client zum Provider, ohne Browser dazwischen: der Back-Channel. Hier wird der code_verifier nachgereicht. Der Provider bildet selbst den SHA-256 darüber und vergleicht ihn mit der Challenge aus Schritt 2. Passt es nicht, gibt es keine Tokens – selbst wenn der Code korrekt ist.
POST HTTP/1.1
Host:
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=
&redirect_uri=
&client_id=
&code_verifier=
Feld
Wert in diesem Durchlauf
Bedeutung
grant_typePflicht
authorization_code
Sagt dem Token-Endpunkt, welche Art Tausch gemeint ist.
codePflicht
Der Code aus dem Rücksprung.
redirect_uriPflicht
Noch einmal derselbe Wert wie in Schritt 2. Der Provider vergleicht ihn – das bindet den Code an die ursprüngliche Anfrage.
client_idPflicht
Bei öffentlichen Clients die einzige Angabe zur Identität.
code_verifierPflicht
Das Gegenstück zur Challenge. Jetzt erst verlässt es den Client – und nur hier.
client_secretbedingt
Nur für vertrauliche Clients. Üblicher als im Body ist HTTP Basic Auth; noch besser ist private_key_jwt, dann geht überhaupt kein Geheimnis über die Leitung.
Ein client_secret gehört ausschließlich in Clients, die es geheim halten können: Server-Anwendungen. In einer SPA oder einer Mobile-App ist es nach dem ersten Blick in den Netzwerk-Tab bzw. in das entpackte Paket kein Geheimnis mehr. Genau dafür gibt es PKCE – ein öffentlicher Client ohne Secret ist damit trotzdem sicher.
Back-ChannelProvider (IdP) → Anwendung200
Die Antwort: drei Tokens mit drei Aufgaben
Der Provider antwortet mit einem JSON-Objekt. Die drei Tokens darin werden ständig verwechselt, haben aber völlig verschiedene Empfänger: Das ID-Token ist für den Client und sagt, wer sich angemeldet hat. Das Access Token ist für die API und sagt, was erlaubt ist – der Client soll es gar nicht lesen. Das Refresh Token ist für den Provider und dient nur dazu, neue Tokens zu bekommen.
Der Schlüssel zur API. Kann ein JWT sein oder eine undurchsichtige Zeichenkette – für den Client ist es in beiden Fällen ein Wert zum Weiterreichen, nicht zum Auswerten.
token_typePflicht
Bearer
Praktisch immer Bearer: Wer es hat, darf es benutzen. Daher sorgsam behandeln.
expires_inempfohlen
3600
Restlaufzeit des Access Tokens in Sekunden, gerechnet ab dieser Antwort.
id_tokenbedingt
Das Ergebnis der Anmeldung, als signiertes JWT. Nur bei scope=openid.
refresh_tokenoptional
Holt neue Tokens, ohne den Nutzer erneut zu fragen. Deutlich langlebiger und damit das schützenswerteste der drei.
scopebedingt
Was tatsächlich gewährt wurde – kann weniger sein als angefragt.
Das ID-Token unten ist ein echtes, zur Laufzeit in deinem Browser signiertes RS256-Token, kein Textbaustein – inklusive korrekt gerechnetem at_hash über das Access Token.
lokalAnwendung
ID-Token prüfen (und das ist Pflicht)
Ein ID-Token, das man nicht prüft, ist eine Behauptung. Die Prüfung ist der Punkt, an dem aus einem Stück Base64 eine belastbare Aussage über eine Identität wird – und sie ist die häufigste Stelle, an der Eigenbau-Integrationen scheitern. Bibliotheken nehmen einem das ab; wer es selbst baut, muss jeden Punkt dieser Liste abhaken.
Feld
Wert in diesem Durchlauf
Bedeutung
issPflicht
Muss exakt der erwartete Aussteller sein – Zeichenvergleich, kein Präfix.
audPflicht
Muss die eigene client_id enthalten. Ein Token für eine andere Anwendung ist kein gültiger Ausweis.
expPflicht
Muss in der Zukunft liegen. Etwas Toleranz für Uhrenversatz ist üblich, mehr als ein paar Minuten nicht.
noncePflicht
Muss dem Wert aus Schritt 1 entsprechen.
azpbedingt
Bei mehreren Audiences: die Anwendung, für die das Token gedacht war.
at_hashbedingt
Die linke Hälfte des SHA-256 über das Access Token, Base64url-kodiert. Bindet beide Tokens aneinander.
Den Algorithmus immer selbst vorgeben, nie aus dem Header des Tokens übernehmen. Sonst schickt ein Angreifer alg: none oder tauscht RS256 gegen HS256 und signiert mit dem öffentlichen Schlüssel, den er ohnehin kennt.
Back-ChannelAnwendung → Provider (IdP)GET
UserInfo abfragen – oft überflüssig
Der UserInfo-Endpunkt liefert dieselben Claims noch einmal, diesmal gegen Vorlage des Access Tokens. Sinnvoll ist er, wenn die Angaben aktuell sein müssen oder das ID-Token schlank bleiben soll – manche Provider liefern Standard-Claims überhaupt nur hier. Enthält das ID-Token bereits alles Benötigte, ist dieser Aufruf ein zusätzlicher Roundtrip ohne Gewinn.
Der sub-Wert aus der Antwort muss dem sub aus dem ID-Token entsprechen. Tut er das nicht, wurden zwei verschiedene Personen vermischt – die Antwort ist dann zu verwerfen.
Back-ChannelAnwendung → Provider (IdP)POST
Nachschub holen, ohne den Nutzer zu stören
Läuft das Access Token ab, tauscht der Client das Refresh Token gegen ein frisches Paar. Für den Nutzer passiert dabei sichtbar nichts. Der angefragte scope darf dabei kleiner werden, aber niemals größer als beim ursprünglichen Zugriff.
POST HTTP/1.1
Host:
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=
&client_id=
&scope=
Feld
Wert in diesem Durchlauf
Bedeutung
grant_typePflicht
refresh_token
Der Tausch eines Refresh Tokens gegen neue Tokens.
refresh_tokenPflicht
Das Token aus der letzten Antwort – bei Rotation jedes Mal ein anderes.
client_idbedingt
Bei öffentlichen Clients erforderlich, bei vertraulichen ersetzt durch die Client-Authentisierung.
scopeoptional
Optional einschränken. Erweitern geht nicht.
Öffentliche Clients müssen Refresh Tokens rotieren: Bei jeder Nutzung gibt es ein neues, das alte verfällt. Taucht ein bereits verbrauchtes Token noch einmal auf, wurde es gestohlen – der Provider zieht dann die ganze Kette ein. Genau deshalb gehört ein Refresh Token in einem Browser niemals in localStorage.
Back-ChannelAnwendung → Provider (IdP)POST
Das Gerät fragt nach einem Code
Ein Fernseher hat keine brauchbare Tastatur und oft gar keinen Browser. Statt den Nutzer dort tippen zu lassen, holt sich das Gerät zwei Codes und verlagert die Anmeldung auf ein Gerät, das dafür taugt – das Telefon in der Hosentasche.
POST HTTP/1.1
Host:
Content-Type: application/x-www-form-urlencoded
client_id=
&scope=
Feld
Wert in diesem Durchlauf
Bedeutung
client_idPflicht
Die Kennung des Geräts bzw. der Anwendung darauf.
scopeoptional
Mit openid gibt es auch hier am Ende ein ID-Token.
Back-ChannelProvider (IdP) → Anwendung200
Zwei Codes: einer für den Menschen, einer für die Maschine
Der user_code ist kurz und absichtlich leicht abzutippen – meist ohne die Zeichen, die man verwechselt. Er wird auf dem Bildschirm angezeigt. Der device_code ist lang, wird nie angezeigt und ist der Wert, mit dem das Gerät gleich pollt.
Das Geheimnis des Geräts. Nur zum Pollen, nie anzeigen.
user_codePflicht
Der kurze Code zum Abtippen. Wird auf dem Bildschirm angezeigt.
verification_uriPflicht
Die Adresse, die der Nutzer auf dem Telefon öffnet.
verification_uri_completeoptional
Dieselbe Adresse mit eingebautem Code – für den QR-Code.
expires_inPflicht
900
Wie lange das Codepaar gilt, typisch 10 bis 15 Minuten.
intervaloptional
5
Mindestabstand zwischen zwei Poll-Versuchen, in Sekunden.
Die verification_uri_complete enthält den user_code bereits – ideal als QR-Code auf dem Fernseher. Der Nutzer scannt und muss nichts mehr eintippen.
Front-ChannelNutzer / Browser → Provider (IdP)
Der Nutzer erledigt die Anmeldung auf dem Telefon
Auf dem Telefon läuft der ganz normale Anmeldeablauf inklusive zweitem Faktor – mit richtiger Tastatur, vertrautem Browser und sichtbarer Adresszeile. Das Gerät im Wohnzimmer sieht davon nichts und bekommt nie ein Passwort zu Gesicht.
Die Schwachstelle des Verfahrens ist Phishing: Ein Angreifer startet den Flow selbst und bringt sein Opfer dazu, dessen Code zu bestätigen. Deshalb muss die Zustimmungsseite deutlich benennen, welches Gerät gerade Zugriff bekommt – und der Flow gehört nur auf Geräte, die ihn wirklich brauchen.
Back-ChannelAnwendung → Provider (IdP)POST
Das Gerät fragt im Takt nach
Währenddessen fragt das Gerät den Token-Endpunkt in regelmäßigen Abständen, ob der Nutzer fertig ist. Die Antwort ist so lange ein Fehler – und der ist hier keine Störung, sondern der vorgesehene Zustand.
POST HTTP/1.1
Host:
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=
&client_id=
HTTP/1.1 400 Bad Request
{ "error": "authorization_pending" }
Feld
Bedeutung
authorization_pendingPflicht
Alles in Ordnung, der Nutzer ist noch nicht fertig. Weiter warten.
slow_downPflicht
Zu schnell gefragt. Abstand erhöhen und erst dann erneut versuchen.
access_deniedPflicht
Der Nutzer hat abgelehnt. Endgültig, nicht erneut versuchen.
expired_tokenPflicht
Das Codepaar ist abgelaufen. Der Flow beginnt von vorn.
slow_down ist kein Vorschlag: Der Abstand muss danach um mindestens fünf Sekunden wachsen, sonst sperrt der Provider den Versuch.
Back-ChannelProvider (IdP) → Anwendung200
Zustimmung erteilt – Tokens für das Gerät
Sobald der Nutzer auf dem Telefon zugestimmt hat, beantwortet der nächste Poll-Versuch sich selbst: Das Gerät bekommt Access Token, Refresh Token und – bei scope=openid – ein ID-Token. Ab hier unterscheidet es sich in nichts mehr vom Code-Flow.
Weil Geräte selten erneut angemeldet werden können, sind Refresh Tokens hier besonders langlebig. Sie liegen auf einem Gerät im Wohnzimmer – die Möglichkeit, sie zentral zurückzuziehen, ist damit Pflicht und nicht Kür.
Back-ChannelAnwendung → Provider (IdP)POST
Ein Dienst weist sich selbst aus
Kein Browser, kein Nutzer, keine Weiterleitung: Ein Hintergrunddienst braucht Zugriff auf eine API und authentisiert sich dafür mit seinen eigenen Zugangsdaten. Der ganze Flow ist ein einziger HTTP-Aufruf. Die client_id ist hier die Identität, es gibt keine Person dahinter.
POST HTTP/1.1
Host:
Authorization: Basic
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&scope=invoices.read
Feld
Wert in diesem Durchlauf
Bedeutung
grant_typePflicht
client_credentials
Der Dienst handelt in eigenem Namen.
scopeoptional
invoices.read
Welche API-Rechte gebraucht werden. Hier kein openid – dazu unten mehr.
client_idbedingt
Die Identität des Dienstes.
client_secretbedingt
Das zugehörige Geheimnis, hier in Basic Auth kodiert.
Die Zugangsdaten gehören in Basic Auth oder – besser – in ein signiertes Client-Assertion-JWT (private_key_jwt). Dann verlässt das Geheimnis den Dienst nie, und ein mitgelesener Aufruf enthält nichts Wiederverwendbares.
Back-ChannelProvider (IdP) → Anwendung200
Ein Access Token – und sonst nichts
Die Antwort enthält nur ein Access Token. Kein ID-Token, kein Refresh Token, und das ist kein Versehen des Providers, sondern richtig so: Ein ID-Token bezeugt, dass sich ein Mensch angemeldet hat. Hier hat sich niemand angemeldet. Ein Refresh Token wäre ebenso sinnlos – der Dienst kennt sein Geheimnis und kann sich jederzeit ein neues Token holen.
Deshalb ist dieser Flow kein OpenID Connect, sondern reines OAuth 2.0. Wer hier scope=openid schickt, bekommt je nach Provider einen Fehler oder ein ID-Token ohne Aussagewert.
Front-ChannelAnwendung → Provider (IdP)GET
Tokens direkt anfordern – der alte Weg
Vor PKCE konnte eine reine Browser-Anwendung den Token-Endpunkt nicht sicher ansprechen: Ein client_secret hätte im Quelltext gestanden, und CORS gab es noch nicht überall. Die Antwort darauf war, die Tokens gleich mit der Weiterleitung auszuliefern – ohne zweiten Schritt.
id_token token liefert beide Tokens sofort, id_token allein nur die Anmeldung.
noncePflicht
Im Implicit Flow zwingend – es ist die einzige Bindung des Tokens an diese Anfrage.
response_modeoptional
fragment | form_post
fragment ist der Standard. form_post liefert die Tokens stattdessen per POST und hält sie damit aus der Adresszeile heraus.
Der Zwischenweg response_type=code id_token heißt Hybrid Flow: Das ID-Token kommt sofort, der Code wird wie gewohnt getauscht. Man trifft ihn noch bei OpenID-Connect-Implementierungen der ersten Stunde und bei Microsoft-Entra-Konfigurationen.
Front-ChannelProvider (IdP) → Anwendung302
Die Tokens stehen in der Adresszeile
Der Teil hinter dem # wird zwar nicht an den Server geschickt, ist aber alles andere als geschützt: Er steht im Browserverlauf, wird von jedem Skript auf der Seite gelesen, landet in Erweiterungen und wird beim Teilen eines Links mitkopiert. Ein Access Token, das durch eine Adresszeile gereicht wurde, muss als kompromittiert gelten.
HTTP/1.1 302 Found
Location: #access_token=
&token_type=Bearer
&expires_in=3600
&id_token=
&state=
Ein Refresh Token gibt es hier nicht – wenn das Access Token abläuft, war die einzige Lösung ein verstecktes iframe mit prompt=none. Das scheitert heute an den Drittanbieter-Cookie-Regeln der Browser und ist damit auch praktisch am Ende.
lokalAnwendung
Warum davon abgeraten wird – und was stattdessen gilt
Die aktuelle Empfehlung der IETF (OAuth 2.0 Security Best Current Practice) ist eindeutig: Der Implicit Flow soll nicht mehr verwendet werden, und in OAuth 2.1 fehlt er schlicht. Für jede Art von Browser-Anwendung gilt heute Authorization Code mit PKCE – dasselbe Verfahren wie für Server-Anwendungen, nur ohne client_secret.
Wenn du auf eine bestehende Integration mit response_type=token triffst: Das ist der Umstellungsgrund, nicht bloß eine Modernisierung. Der Weg dahin ist meist kurz, weil jede aktuelle Bibliothek Code+PKCE ohnehin als Standard fährt.
Das ID-Token aus diesem Durchlauf
Kein Textbaustein: Dieses Token wurde eben in deinem Browser mit einem frisch erzeugten RSA-Schlüssel signiert. Der at_hash passt wirklich zum Access Token oben, und mit dem JWK darunter lässt sich die Signatur prüfen.
Dieser Ablauf liefert kein ID-Token – es meldet sich niemand an, über den etwas zu bezeugen wäre.
Wer das Token ausgestellt hat. Wird bei der Prüfung zeichengenau verglichen.
sub
Die unveränderliche Kennung des Nutzers beim Provider. Als Primärschlüssel geeignet – anders als die E-Mail-Adresse, die sich ändern kann.
aud
Für wen das Token bestimmt ist. Muss die eigene client_id enthalten.
exp
Ab wann das Token nicht mehr gilt.
iat
Wann es ausgestellt wurde.
auth_time
Wann sich der Nutzer tatsächlich angemeldet hat – kann deutlich früher liegen, wenn eine Sitzung wiederverwendet wurde.
nonce
Der Wert aus der Anfrage. Bindet dieses Token an genau diesen Anmeldevorgang.
azp
Die Partei, für die das Token gedacht ist. Relevant, wenn aud mehrere Einträge hat.
at_hash
Abdruck des Access Tokens. Belegt, dass beide Tokens zusammengehören.
sid
Die Sitzungs-ID beim Provider. Wird für Logout-Benachrichtigungen gebraucht.
name
Anzeigename. Gehört zum scope profile.
given_name
Vorname, ebenfalls aus profile.
family_name
Nachname, ebenfalls aus profile.
email
E-Mail-Adresse. Nur mit scope email.
email_verified
Ob der Provider die Adresse geprüft hat. Ohne dieses true taugt die Adresse nicht zur Zuordnung eines Kontos.
Passender JWKS-Eintrag (jwks_uri)
Deinen eigenen Provider einlesen
Jeder OpenID-Connect-Provider beschreibt sich selbst unter /.well-known/openid-configuration. Lade das Dokument oder füge es ein – die Felder werden erklärt, und die Endpunkte lassen sich in die Simulation oben übernehmen.
Was daraus folgt
Die Felder im Einzelnen
Feld
Wert in diesem Durchlauf
Bedeutung
Hinweis: „Laden“ ist der einzige Punkt in diesem Werkzeug, an dem dein Browser eine fremde Adresse aufruft – und nur, wenn du darauf klickst. Die Anfrage geht direkt von dir zum Provider, nicht über unseren Server. Eingefügtes JSON verlässt deinen Browser überhaupt nicht.
OAuth 2.0 oder OpenID Connect – was ist was?
Die beiden werden dauernd in einen Topf geworfen, beantworten aber verschiedene Fragen.OAuth 2.0 ist ein Berechtigungsverfahren: Es regelt, wie eine Anwendung Zugriff auf fremde Daten bekommt, ohne das Passwort zu kennen – „diese App darf meinen Kalender lesen“. Über den Menschen dahinter sagt es bewusst nichts.
OpenID Connect ist eine dünne Schicht darüber und ergänzt genau das fehlende Stück: die Anmeldung. Technisch sind es im Wesentlichen drei Zutaten – der Scope openid, ein zusätzliches Token in der Antwort (das ID-Token) und ein festgelegter Satz von Endpunkten. Alles andere ist OAuth. Wer den Code-Flow von OAuth verstanden hat, versteht OIDC in fünf Minuten mit.
Die praktische Konsequenz steht in der Fehlerliste vieler Projekte: Ein Access Token istkein Anmeldenachweis. Es bezeugt eine Erlaubnis, nicht eine Identität, und es ist nicht für die Anwendung ausgestellt, sondern für die API dahinter. Wer sich mit einem Access Token anmeldet, baut sich eine Lücke ein – das ID-Token ist das Stück, das dafür vorgesehen ist.
Welchen Ablauf brauche ich?
Anwendung
Ablauf
Warum
Serveranwendung
Authorization Code + PKCE
Kann ein client_secret wirklich geheim halten. PKCE trotzdem mitnehmen – es kostet nichts und schützt gegen abgefangene Codes.
SPA im Browser
Authorization Code + PKCE
Öffentlicher Client, also ohne Secret. PKCE ersetzt es vollständig.
Mobile- oder Desktop-App
Authorization Code + PKCE
Im Systembrowser, nicht im eingebetteten WebView – sonst sieht die App das Passwort doch wieder.
Fernseher, Konsole, CLI
Device Authorization Grant
Verlagert die Eingabe auf ein Gerät mit Tastatur und Browser.
Hintergrunddienst, Cronjob
Client Credentials
Kein Mensch beteiligt, also auch keine Anmeldung und kein ID-Token.
Bestandssystem mit response_type=token
Implicit – umstellen
Gilt als überholt und fehlt in OAuth 2.1. Der Ersatz ist derselbe Code-Flow wie überall sonst.
state, nonce und PKCE: drei Zufallswerte, drei verschiedene Aufgaben
Sie sehen gleich aus – alle drei sind Zufallszeichenketten, die hin- und zurückgehen – und werden deshalb regelmäßig verwechselt oder als redundant abgetan. Sie sichern aber drei völlig verschiedene Dinge ab, und keiner ersetzt einen anderen.
Wert
Liegt bei
Verhindert
state
Client-Sitzung
Dass jemand dem Nutzer einen fremden Rücksprung unterschiebt und ihn so in ein fremdes Konto einloggt (CSRF).
nonce
Client-Sitzung, später im ID-Token
Dass ein anderswo erbeutetes, gültiges ID-Token ein zweites Mal eingespielt wird.
code_verifier
Nur im Client
Dass ein abgefangener Autorisierungscode von jemand anderem eingelöst wird.
Der Unterschied lässt sich auch am Weg festmachen: state und nonce reisen offen durch den Browser und werden beim Zurückkommen verglichen. Der code_verifierreist überhaupt nicht durch den Browser – unterwegs ist nur sein Abdruck. Deshalb ist PKCE der einzige der drei Mechanismen, der auch dann noch trägt, wenn ein Angreifer den kompletten Front-Channel mitliest.
ID-Token, Access Token, Refresh Token
Token
Empfänger
Beantwortet
Lebensdauer
ID-Token
der Client
Wer hat sich angemeldet, wann und wie?
Minuten – es wird einmal geprüft, nicht aufbewahrt
Access Token
die API
Was darf der Aufrufer?
Minuten bis Stunden
Refresh Token
der Provider
Darf ich neue Tokens haben?
Tage bis Monate
Daraus folgen zwei Regeln, die oft verletzt werden. Erstens: Ein ID-Token gehört niemals alsAuthorization-Header an eine API. Es ist für den Client ausgestellt (audist die client_id), und eine API, die es trotzdem akzeptiert, prüft die falsche Zielgruppe. Zweitens: Ein Client soll das Access Token nicht auswerten. Es mag ein lesbares JWT sein, muss es aber nicht – das Format gehört dem Provider und der API und kann sich jederzeit ändern.
Scopes und Claims
Ein Scope ist das, wonach gefragt wird; ein Claim das, was zurückkommt. OpenID Connect definiert dafür feste Pakete: profile bringt Name, Bild und Sprache mit, email die Adresse samt email_verified,address und phone entsprechend. offline_access ist der Sonderfall – es fragt nicht nach Daten, sondern nach einem Refresh Token.
Bei email_verified lohnt ein zweiter Blick: Ist der Wert nicht true, darf die Adresse nicht zur Zuordnung eines bestehenden Kontos dienen. Andernfalls genügt es, bei einem Provider ohne Adressprüfung ein Konto mit fremder Adresse anzulegen, um sich in das Konto eines anderen einzuloggen. Stabil ist allein die Kombination aus iss und sub.
Discovery: die Selbstauskunft des Providers
Unter /.well-known/openid-configuration beschreibt jeder OpenID-Connect-Provider sich selbst: Endpunkte, unterstützte Scopes, Signaturverfahren, PKCE-Methoden. Für eine Integration ist das die zuverlässigste Quelle – zuverlässiger als die Dokumentation, denn das Dokument kommt aus dem laufenden System. Das Werkzeug oben liest es ein, erklärt jedes Feld und beantwortet daraus gleich die üblichen Fragen: Wird S256 unterstützt? Gibt es den Device Flow? Werden unsignierte ID-Tokens angeboten (was ein Warnsignal wäre)?
Ein Detail entscheidet über die Sicherheit der ganzen Kette: Der issuer im Dokument muss zu der Adresse passen, unter der das Dokument liegt. Später wird der iss-Claim jedes ID-Tokens genau gegen diesen Wert geprüft – zeichengenau, nicht „fängt an mit“.
Abmelden ist schwerer als anmelden
Der lokale Logout ist trivial: Sitzung des Clients verwerfen, fertig. Nur ist der Nutzer damit weiterhin beim Provider angemeldet – der nächste Anmeldeversuch läuft ohne jede Rückfrage durch und wirkt wie ein Fehler. Dafür gibt es das RP-Initiated Logout über denend_session_endpoint, mit id_token_hint undpost_logout_redirect_uri.
Umgekehrt ist es noch unangenehmer: Meldet sich jemand beim Provider ab, sollen es alle angeschlossenen Anwendungen erfahren. Der Front-Channel-Weg dafür (versteckte iframes) hängt an Drittanbieter-Cookies und verschwindet mit ihnen; verlässlich ist nur Back-Channel Logout, bei dem der Provider jede Anwendung serverseitig benachrichtigt. Dafür braucht der Client einen erreichbaren Endpunkt – eine reine SPA ohne Backend kann das nicht leisten.
Häufige Fragen
Wird hier irgendetwas an einen Server geschickt? Nein. Der Provider im Werkzeug ist nachgestellt, alle Werte entstehen in deinem Browser. Die einzige Ausnahme ist die Schaltfläche „Laden“ im Discovery-Bereich: Sie ruft die Adresse auf, die du dort einträgst – direkt von deinem Browser zu diesem Provider, nicht über unseren Server. Eingefügtes JSON verlässt deinen Browser gar nicht.
Kann ich das Werkzeug gegen meinen echten Provider laufen lassen? Nicht als vollständigen Anmeldevorgang – dafür bräuchte es eine registrierteredirect_uri und hieße, fremde Tokens in einer fremden Seite entgegenzunehmen. Was du tun kannst: das Discovery-Dokument einlesen, die Endpunkte übernehmen und die entstehenden Anfragen mit denen deiner eigenen Anwendung vergleichen. Erfahrungsgemäß findet man den Unterschied genau dort – ein fehlender nonce, ein redirect_uri, der sich um einen Schrägstrich unterscheidet.
Ist das ID-Token oben echt? Ja. Es wird beim Laden der Seite mit einem frisch erzeugten RSA-Schlüssel über die Web-Crypto-API signiert; der ausgegebene JWK-Satz gehört dazu und prüft die Signatur erfolgreich. Auch derat_hash ist wirklich über das daneben stehende Access Token gerechnet. Zum Gegenprüfen: JWT-Decoder.
Warum liefert Client Credentials kein ID-Token? Weil es niemanden gibt, über den etwas zu bezeugen wäre. Ein ID-Token ist die Aussage „diese Person hat sich hier und jetzt angemeldet“. Bei einem Hintergrunddienst gibt es keine Person, nur eine Anwendung, die sich selbst ausweist. Deshalb ist dieser Ablauf reines OAuth 2.0 und gar kein OpenID Connect.
Brauche ich PKCE auch mit einem client_secret? Ja, und die Empfehlung der IETF ist hier eindeutig. Das Secret schützt den Token-Endpunkt, PKCE schützt den Autorisierungscode auf seinem Weg durch den Browser – zwei verschiedene Strecken. Der Aufwand dafür sind zwei zusätzliche Parameter, die jede Bibliothek ohnehin schon setzt.