Skip to Content

Autorisierungscode + PKCE-Flow

Der Autorisierungscode + PKCE-Flow ist das empfohlene OAuth 2.0-Muster für alle Anwendungen, die echte Benutzer authentifizieren. Diese Seite erklärt, was PKCE ist, warum es eingeführt wurde und wie der vollständige Flow von Anfang bis Ende funktioniert — einschließlich der Sicherheitseigenschaften bei jedem Schritt.

Warum PKCE existiert

Der ursprüngliche OAuth 2.0-Autorisierungscode-Flow (ohne PKCE) hat eine Sicherheitsschwachstelle bei öffentlichen Clients: Code-Abfangen.

Wenn Auris mit einem Autorisierungscode zurück zu deiner Anwendung weiterleitet (Schritt 4 im klassischen Flow), reist dieser Code durch die URL-Leiste des Browsers. Auf mobilen Geräten könnte eine bösartige Anwendung, die für das gleiche benutzerdefinierte URI-Schema registriert ist, die Weiterleitung abfangen. Selbst bei Web-Anwendungen kann der Code in Server-Protokollen, Referrer-Headern oder dem Browser-Verlauf erscheinen.

Wenn ein Angreifer den Code abfängt, kann er ihn gegen Tokens am Token-Endpunkt austauschen. Im klassischen Flow gibt es nichts, das ihn daran hindert.

PKCE (Proof Key for Code Exchange, RFC 7636) behebt dies, indem der Autorisierungscode an die spezifische Client-Sitzung gebunden wird, die ihn angefordert hat. Nur der Client, der ursprünglich den Code-Verifier generiert hat, kann den Code austauschen — selbst wenn ein Angreifer den Code selbst hat.

Auris erzwingt PKCE bei allen Autorisierungscode-Flows. Die plain-Challenge-Methode wird abgelehnt — nur die S256-Methode (SHA-256) wird akzeptiert. Es gibt keine Möglichkeit, PKCE in Auris zu umgehen.

Der PKCE-Mechanismus

PKCE fügt dem Standard-Flow zwei Werte hinzu:

Code Verifier: Ein kryptografisch zufälliger String, 43-128 Zeichen lang, der nur nicht reservierte URL-Zeichen verwendet (A-Z, a-z, 0-9, -, ., _, ~). Dieser Wert wird vom Client geheim gehalten.

Code Challenge: Eine transformierte Version des Code Verifiers, berechnet als:

code_challenge = BASE64URL(SHA256(ASCII(code_verifier)))

Der Client sendet den Code Challenge (den Hash) an den Autorisierungs-Endpunkt. Beim Austausch des Codes sendet der Client den rohen Code Verifier. Auris berechnet den Challenge neu und bestätigt, dass er übereinstimmt. Da ein Angreifer einen SHA-256-Hash nicht umkehren kann, kann er den Verifier nicht fälschen.

Schritt-für-Schritt-Flow

Schritt 1: Code Verifier generieren

Der Client generiert einen kryptografisch zufälligen code_verifier. Dieser muss 43-128 Zeichen aus dem Alphabet [A-Za-z0-9-._~] bestehen.

function generateCodeVerifier(): string { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~' const array = new Uint8Array(64) crypto.getRandomValues(array) return Array.from(array) .map(b => chars[b % chars.length]) .join('') } const codeVerifier = generateCodeVerifier() // Beispiel: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

Speichere den code_verifier in sessionStorage (nicht localStorage) — er muss nur den Weiterleitungs-Roundtrip überleben.

Schritt 2: Code Challenge ableiten

Berechne SHA-256(code_verifier) und Base64URL-kodiere das Ergebnis:

async function generateCodeChallenge(verifier: string): Promise<string> { const encoder = new TextEncoder() const data = encoder.encode(verifier) const digest = await crypto.subtle.digest('SHA-256', data) return btoa(String.fromCharCode(...new Uint8Array(digest))) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, '') } const codeChallenge = await generateCodeChallenge(codeVerifier) // Beispiel: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

Schritt 3: State generieren

Generiere einen zufälligen state-Parameter für den CSRF-Schutz. Dieser Wert wird an den Authorization Server gesendet und muss verifiziert werden, wenn der Code zurückgegeben wird.

function generateState(): string { const array = new Uint8Array(16) crypto.getRandomValues(array) return btoa(String.fromCharCode(...array)) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, '') } const state = generateState() sessionStorage.setItem('pkce_state', state) sessionStorage.setItem('pkce_code_verifier', codeVerifier)

Schritt 4: Zum Autorisierungs-Endpunkt weiterleiten

Erstelle die Autorisierungs-URL und leite den Browser weiter:

const params = new URLSearchParams({ response_type: 'code', client_id: 'your-client-id', redirect_uri: 'https://app.ihredomain.com/callback', state: state, code_challenge: codeChallenge, code_challenge_method: 'S256', scope: 'openid profile email', }) window.location.href = `https://api.altovar.net/api/oauth/authorize?${params}`

Der Browser navigiert zur gehosteten Auris-Login-Seite. Ab diesem Punkt interagiert der Benutzer direkt mit Auris — deine Anwendung ist nicht beteiligt.

Schritt 5: Benutzer authentifiziert sich bei Auris

Auf der gehosteten Auris-Login-Seite:

  • Gibt der Benutzer seine E-Mail und sein Passwort ein (oder verwendet Social Login, Magic Link usw.)
  • Schließt MFA ab, falls von der Tenant-Richtlinie oder adaptiver Risikobewertung erfordert
  • Sieht einen Zustimmungsbildschirm, wenn die Anwendung sensible Scopes anfordert (nur beim ersten Mal)

Dein Anwendungscode läuft während dieses Schritts nicht.

Schritt 6: Auris leitet mit Code zurück

Bei erfolgreicher Authentifizierung stellt Auris einen kurzlebigen Autorisierungscode (5 Minuten) aus und leitet den Browser zurück zu deiner redirect_uri:

https://app.ihredomain.com/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz789abc

Der Autorisierungscode ist einmalig verwendbar. Jeder Versuch, ihn zweimal zu verwenden, wird abgelehnt.

Schritt 7: State validieren (CSRF-Schutz)

Vergleiche, bevor du irgendetwas mit dem Code tust, den im Query-String zurückgegebenen state mit dem in sessionStorage gespeicherten:

const returnedState = new URLSearchParams(window.location.search).get('state') const storedState = sessionStorage.getItem('pkce_state') if (!returnedState || returnedState !== storedState) { throw new Error('State-Nichtübereinstimmung — möglicher CSRF-Angriff') }

Dies stellt sicher, dass die Weiterleitung auf deine ursprüngliche Anfrage antwortet, nicht auf eine Cross-Site-Request-Injektion.

Schritt 8: Code gegen Tokens tauschen

Rufe den gespeicherten code_verifier ab und sende ihn zusammen mit dem Autorisierungscode an den Token-Endpunkt:

const code = new URLSearchParams(window.location.search).get('code') const codeVerifier = sessionStorage.getItem('pkce_code_verifier') const response = await fetch('https://api.altovar.net/api/auth/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ grant_type: 'authorization_code', code: code, code_verifier: codeVerifier, redirect_uri: 'https://app.ihredomain.com/callback', client_id: 'your-client-id', }), }) const data = await response.json()

Auris verifiziert: SHA256(code_verifier) === stored_code_challenge. Wenn sie übereinstimmen, werden Tokens ausgestellt.

Schritt 9: Tokens empfangen

{ "ok": true, "data": { "accessToken": "eyJhbGciOiJSUzI1NiJ9...", "refreshToken": "rt_...", "idToken": "eyJhbGciOiJSUzI1NiJ9...", "expiresIn": 900, "tokenType": "Bearer" } }

Bereinige die PKCE-Werte aus sessionStorage:

sessionStorage.removeItem('pkce_state') sessionStorage.removeItem('pkce_code_verifier')

Speichere die Tokens entsprechend deiner Sicherheitsanforderungen (siehe Best Practices zur Token-Speicherung).

Sequenzdiagramm

AppBrowserAuris1Generate verifier + challengeStore verifier in sessionStorageGET /oauth/authorize?code_challenge=xxx&state=yyy3Store challenge + stateShow hosted login page4User authenticatesRedirect to redirect_uri?code=zzz&state=yyy6Validate state (CSRF check)POST /auth/token{ code, code_verifier, client_id }8SHA256(verifier) == challenge?Yes → issue tokens{ accessToken, refreshToken, idToken }

Sicherheitseigenschaften

Eigenschaft 1: Widerstand gegen Code-Abfangen

Wenn ein Angreifer den Autorisierungscode in Schritt 6 abfängt (über URL-Sniffing, bösartige App oder Log-Scraping), kann er ihn trotzdem nicht gegen Tokens austauschen. Um den Code auszutauschen, benötigen sie den code_verifier. Der code_verifier wird bis Schritt 8 nie an Auris gesendet — und in diesem Punkt geht er direkt über HTTPS an den Token-Endpunkt, nicht durch die Browser-URL.

Eigenschaft 2: CSRF-Schutz über State

Der state-Parameter ist ein zufälliger Wert, der von deinem Client generiert und von Auris unverändert zurückgegeben wird. Wenn ein Angreifer eine bösartige Weiterleitung zu deiner Callback-URL mit einem gefälschten Code erstellt, wird deine Anwendung erkennen, dass der State nicht mit dem gespeicherten übereinstimmt, und den Flow ablehnen.

Eigenschaft 3: Einmalig verwendbare Autorisierungscodes

Jeder Autorisierungscode kann nur einmal verwendet werden. Wenn Auris einen Code-Replay-Versuch erkennt (derselbe Code zweimal verwendet), lehnt es die zweite Anfrage ab und kann bereits für diesen Code ausgestellte Tokens ungültig machen.

Eigenschaft 4: Weiterleitungs-URI-Bindung

Die beim Austausch verwendete redirect_uri muss genau mit der in der Auris-Konsole für diese Anwendung registrierten übereinstimmen. Ein Angreifer kann keine eigene Weiterleitungs-URI registrieren und Codes dorthin leiten lassen.

Eigenschaft 5: Kurze Code-Lebensdauer

Autorisierungscodes laufen nach 5 Minuten ab. Dies begrenzt das Zeitfenster, in dem ein abgefangener Code missbraucht werden könnte.

Verwendung des Auris SDK

Wenn du ein Auris SDK verwendest, wird alles oben Genannte automatisch behandelt. Du musst PKCE-Parameter, Speicherung oder den Token-Austausch nicht manuell verwalten:

import { AurisClient } from '@auris/js' const auris = new AurisClient({ domain: 'your-auris-domain.com', clientId: 'your-client-id', redirectUri: 'https://app.ihredomain.com/callback', autoRefresh: true, }) // Beim Klick auf deinen Login-Button: await auris.loginWithRedirect() // Auf deiner Callback-Seite: const result = await auris.handleRedirectCallback() console.log(result.user) // Der authentifizierte Benutzer

Das SDK generiert Code Verifier und Challenge, speichert sie in sessionStorage, validiert den State beim Callback, tauscht den Code aus und speichert die resultierenden Tokens automatisch.

Das Auris SDK verwendet crypto.subtle.digest('SHA-256', ...) (Web Crypto API) für die S256-Challenge-Berechnung in Browsern und crypto.createHash('sha256') in Node.js. Beide sind kryptografisch sichere native Implementierungen — keine Drittanbieter-Krypto-Abhängigkeit erforderlich.

Verwandte Konzepte