Skip to Content

Device Authorization Flow

Das Problem: Geräte ohne Tastaturen

Der häufigste Grant-Typ von OAuth 2.0 — Authorization Code mit PKCE — setzt voraus, dass der Client einen Browser öffnen, eine Login-Seite anzeigen und Tastatureingaben des Benutzers akzeptieren kann. Diese Annahme schlägt für eine ganze Kategorie von Geräten fehl:

GerätWarum Standard-OAuth scheitert
Smart-TVsKeine Tastatur; Bildschirmtastaturen sind für die Passworteingabe mühsam
CLI-ToolsKein Browser; reine Terminal-Schnittstelle
SpielkonsolenController-Eingabe; URLs und Anmeldedaten eingeben ist unpraktisch
IoT-GeräteKein Display überhaupt oder ein minimales Display (z. B. LED-Matrix)
Digitale Beschilderung / KioskeGesperrte Umgebung; keine Browser-Navigation erlaubt
Streaming-Dongles (Chromecast, Fire Stick)Nur Fernbedienung; keine Tastatur

Diese Geräte müssen Benutzer authentifizieren, können aber kein Login-Formular hosten. Der Benutzer muss sich woanders authentifizieren — auf seinem Telefon oder Laptop — und das Gerät muss erfahren, dass die Authentifizierung erfolgreich war.

Genau das löst der OAuth 2.0 Device Authorization Grant (RFC 8628).

Funktionsweise des Device-Flows

Der Device Authorization Grant führt ein sekundäres Gerät-Muster ein: Das Client-Gerät zeigt einen kurzen Code an, der Benutzer gibt diesen Code auf einem Gerät mit Browser (seinem Telefon oder Laptop) ein, und das Client-Gerät pollt den Autorisierungsserver, bis der Benutzer die Authentifizierung abgeschlossen hat.

Der Flow Schritt für Schritt

Schritt 1: Client fordert Geräte- und Benutzercodes an

Der Client sendet eine POST-Anfrage an den Geräteautorisierungs-Endpunkt mit seiner client_id und dem angeforderten scope:

POST /api/oauth/device HTTP/1.1 Host: auth.example.com Content-Type: application/x-www-form-urlencoded client_id=tv-app-client-id &scope=openid profile email

Schritt 2: Server gibt Gerätecode, Benutzercode und Verifizierungs-URI zurück

Der Autorisierungsserver antwortet mit einem JSON-Objekt, das alles für den Flow enthält:

{ "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", "user_code": "WDJB-MJHT", "verification_uri": "https://auth.example.com/device", "verification_uri_complete": "https://auth.example.com/device?user_code=WDJB-MJHT", "expires_in": 600, "interval": 5 }
FeldBeschreibung
device_codeEin langer, kryptographisch zufälliger String, den der Client beim Polling verwendet. Wird dem Benutzer nie angezeigt.
user_codeEin kurzer, menschenlesbarer Code, den der Benutzer auf der Verifizierungsseite eingibt.
verification_uriDie URL, die der Benutzer besucht, um den Code einzugeben. Muss kurz und einprägsam sein.
verification_uri_completeDie vollständige URL mit vorausgefülltem Benutzercode (für QR-Codes).
expires_inWie lange Geräte- und Benutzercodes gültig sind (Sekunden). Standard: 600 (10 Minuten).
intervalMinimales Polling-Intervall in Sekunden. Der Client darf nicht häufiger pollen als dies.

Schritt 3: Benutzer authentifiziert sich auf seinem sekundären Gerät

Das Client-Gerät zeigt dem Benutzer user_code und verification_uri an. Je nach Gerätefähigkeiten kann dies sein:

  • CLI-Tool: Druckt die URL und den Code im Terminal
  • Smart-TV: Zeigt einen großen QR-Code (kodiert verification_uri_complete) neben dem Textcode
  • IoT-Gerät: Zeigt den Code auf einem LED-Bildschirm, mit der URL auf dem Gerätegehäuse gedruckt

Der Benutzer öffnet verification_uri auf seinem Telefon oder Laptop, gibt den user_code ein, meldet sich mit seinen Anmeldedaten an (einschließlich MFA, wenn erforderlich) und genehmigt das Gerät.

Schritt 4: Client pollt den Token-Endpunkt

Während sich der Benutzer authentifiziert, pollt das Client-Gerät den Token-Endpunkt im konfigurierten interval:

POST /api/auth/token HTTP/1.1 Host: auth.example.com Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:device_code &device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS &client_id=tv-app-client-id

Schritt 5: Server antwortet basierend auf der Benutzeraktion

Der Server antwortet mit einem von mehreren Ergebnissen, abhängig vom aktuellen Status:

AntwortHTTP-StatusBedeutungClient-Aktion
authorization_pending400Benutzer hat die Authentifizierung noch nicht abgeschlossenIm konfigurierten Intervall weiter pollen
slow_down400Client pollt zu häufigPolling-Intervall um 5 Sekunden erhöhen
expired_token400Der device_code ist abgelaufen (Benutzer hat zu lange gebraucht)Flow von Schritt 1 neu starten
access_denied400Benutzer hat die Autorisierungsanfrage explizit abgelehntFehler dem Benutzer anzeigen; nicht wiederholen
Erfolg (Zugriffstoken)200Benutzer hat die Anfrage genehmigtTokens speichern; Authentifizierung ist abgeschlossen
// Ausstehende Antwort { "error": "authorization_pending", "error_description": "The authorization request is still pending." } // Verlangsamungsantwort { "error": "slow_down", "error_description": "Polling too frequently. Increase interval by 5 seconds." } // Erfolgsantwort { "access_token": "eyJhbGciOiJSUzI1NiJ9...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...", "scope": "openid profile email" }

Die slow_down-Antwort ist nicht nur informativ — sie ist eine harte Anforderung. Wenn ein Client slow_down erhält, muss er sein Polling-Intervall um mindestens 5 Sekunden erhöhen. Clients, die dies ignorieren und weiterhin im ursprünglichen Tempo pollen, könnten ihren device_code widerrufen lassen.

Design des Benutzercodes

Der user_code ist das wichtigste UX-Element im Device-Flow. Es ist das einzige, was der Benutzer manuell eingibt, daher beeinflusst sein Design direkt die Erfolgsrate.

Anforderungen

RFC 8628 legt fest, dass Benutzercodes sein müssen:

  1. Kurz: Einfach auf einer Telefontatstatur einzutippen (8 Zeichen ist der Sweet-Spot)
  2. Eindeutig: Keine Zeichen, die gleich aussehen (vermeide 0/O, 1/I/l, 5/S, 2/Z)
  3. Groß-/Kleinschreibungsunempfindlich: Benutzer sollten sich keine Gedanken über Groß-/Kleinschreibung machen
  4. Gruppierbar: Ein Bindestrich oder Leerzeichen in der Mitte hilft der Lesbarkeit (WDJB-MJHT vs WDJBMJHT)

Auris-Benutzercode-Alphabet

Auris generiert Benutzercodes aus einem eingeschränkten Alphabet von 20 Zeichen:

B C D F G H J K M N P Q R T V W X Y

Dies schließt alle Vokale aus (verhindert zufällige Generierung anstößiger Wörter) und alle mehrdeutigen Zeichen. Mit einem 8-Zeichen-Code aus einem 20-Zeichen-Alphabet:

20^8 = 25.600.000.000 mögliche Codes (~25,6 Milliarden)

Bei einer Rate von 1.000 Versuchen pro Sekunde (bereits weit über dem, was Rate-Limiting erlauben würde) würde das Brute-Force-Raten eines gültigen Codes im Durchschnitt etwa 296 Tage dauern. Kombiniert mit der 10-minütigen Standard-Ablaufzeit ist die Wahrscheinlichkeit eines erfolgreichen Brute-Force-Angriffs vernachlässigbar.

Benutzercodes müssen einmalig verwendet werden und prompt ablaufen. Auris löscht den DeviceCode-Datensatz, sobald der Benutzer die Anfrage genehmigt oder ablehnt, oder wenn der expires_in-Zeitraum abläuft. Verlängere die Lebensdauer eines Gerätecodes niemals über das hinaus, was die UX erfordert.

Gerätecode-Sicherheit

Während der user_code kurz und menschenlesbar ist, ist der device_code ein langer, kryptographisch zufälliger String, der als Anmeldedaten des Clients beim Polling dient. Er wird dem Benutzer nie angezeigt.

EigenschaftBenutzercodeGerätecode
Länge8 Zeichen40+ Zeichen
Alphabet20 GroßbuchstabenVollständiges URL-sicheres Base64
Entropie~34,6 Bit~240 Bit
Dem Benutzer angezeigtJaNein
Server-seitig gespeichertKlartext (für Benutzercode-Abgleich)SHA-256-Hash (wie Passwörter)
ZweckBenutzeridentifikationClient-Authentifizierung beim Polling

Auris speichert Gerätecodes als SHA-256-Hashes, nicht im Klartext. Wenn die Datenbank kompromittiert wird, kann der Angreifer keine gültigen Gerätecodes aus den Hashes rekonstruieren.

Sicherheitsanalyse

Phishing-Resistenz

Der Device-Flow ist inhärent anfällig für einen spezifischen Phishing-Angriff: Ein Angreifer startet einen Device-Flow auf seinem eigenen Gerät und verleitet dann einen Benutzer dazu, den Benutzercode des Angreifers auf der legitimen Verifizierungsseite einzugeben. Wenn der Benutzer genehmigt, erhält das Gerät des Angreifers Tokens, die an das Benutzerkonto gebunden sind.

Gegenmaßnahmen:

  1. Klare Einwilligungsseite: Die Verifizierungsseite muss klar angeben, dass die Genehmigung einer bestimmten Anwendung/einem Gerät Zugriff gewährt. Auris zeigt den Anwendungsnamen, angeforderte Scopes und eine Warnung an, dass der Benutzer nur fortfahren sollte, wenn er die Anfrage initiiert hat.
  2. verification_uri_complete: Wenn der Benutzer einen QR-Code scannt, ist der Code vorausgefüllt. Der Benutzer sollte überprüfen, ob er mit dem übereinstimmt, was sein Gerät anzeigt.
  3. Kurze Lebensdauer: Das 10-Minuten-Fenster begrenzt das Angriffsfenster.
  4. Rate-Limiting: Auris begrenzt Gerätecode-Anfragen pro Client, um Massenerzeugungs-Angriffe zu verhindern.

Vergleich mit anderen Grant-Typen

Grant-TypBenötigte EingabeBenutzer anwesendBrowser benötigtAm besten für
Authorization Code + PKCEBrowser + TastaturJaJa (auf gleichem Gerät)Web-Apps, mobile Apps
Device AuthorizationNur DisplayJa (auf sekundärem Gerät)Ja (auf sekundärem Gerät)Smart-TVs, CLI-Tools, IoT
Client CredentialsKeineNeinNeinServer-zu-Server (M2M)
CIBAKeine (Push-Benachrichtigung)Ja (auf Benachrichtigungsgerät)NeinCall-Center, Zahlungsgenehmigung

Hauptunterschiede zu CIBA:

  • Device-Flow verlangt, dass der Benutzer aktiv eine URL besucht und einen Code eingibt. Der Benutzer initiiert die Sekundärgeräte-Interaktion.
  • CIBA sendet eine Push-Benachrichtigung an den Benutzer. Der Autorisierungsserver initiiert die Sekundärgeräte-Interaktion.
  • Device-Flow funktioniert für anonyme Benutzer — keine vorherige Registrierung für die Benachrichtigungszustellung ist erforderlich.
  • CIBA erfordert, dass der Autorisierungsserver weiß, wie er den Benutzer erreichen kann (E-Mail, Telefon, Push-Benachrichtigung).

Auris-Implementierungsdetails

Prisma-Modell

Der Device-Flow-Status wird über das DeviceCode-Prisma-Modell verwaltet:

model DeviceCode { id String @id @default(cuid()) tenantId String applicationId String deviceCode String @unique // SHA-256-Hash userCode String @unique // Klartext für Abgleich scope String? status DeviceCodeStatus @default(PENDING) expiresAt DateTime interval Int @default(5) userId String? // gesetzt, wenn Benutzer genehmigt lastPolledAt DateTime? createdAt DateTime @default(now()) } enum DeviceCodeStatus { PENDING APPROVED DENIED EXPIRED }

Konfiguration

Der Device-Flow wird pro Anwendung in der Auris-Konsole unter Anwendungen > (Anwendung auswählen) > Einstellungen aktiviert:

EinstellungStandardBeschreibung
Device-Flow aktivierenfalseHaupt-Toggle
Benutzercode-Länge8Anzahl der Zeichen im Benutzercode
Code-Lebensdauer600Sekunden, bevor Geräte-/Benutzercodes ablaufen
Polling-Intervall5Minimale Sekunden zwischen Token-Endpunkt-Polls

API-Endpunkte

EndpunktMethodeBeschreibung
/api/oauth/devicePOSTGeräte- und Benutzercodes anfordern
/api/auth/tokenPOSTToken-Endpunkt (unterstützt device_code Grant-Typ)
/hosted/deviceGETBenutzerorientierte Verifizierungsseite

Codebeispiel: CLI-Tool mit Device-Flow

Das folgende Beispiel zeigt ein CLI-Tool, das den vollständigen Device-Flow implementiert:

async function loginWithDeviceFlow(clientId: string, domain: string) { // Schritt 1: Gerätecodes anfordern const deviceResponse = await fetch(`${domain}/api/oauth/device`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ client_id: clientId, scope: 'openid profile email', }), }) const { device_code, user_code, verification_uri, verification_uri_complete, interval, expires_in, } = await deviceResponse.json() // Schritt 2: Anweisungen dem Benutzer anzeigen console.log('\n Um dich anzumelden, besuche:', verification_uri) console.log(' und gib den Code ein: ', user_code) console.log(`\n Oder öffne: ${verification_uri_complete}`) console.log(`\n Dieser Code läuft in ${Math.floor(expires_in / 60)} Minuten ab.\n`) // Schritt 3: Auf Abschluss pollen let pollInterval = interval * 1000 const deadline = Date.now() + expires_in * 1000 while (Date.now() < deadline) { await new Promise(resolve => setTimeout(resolve, pollInterval)) const tokenResponse = await fetch(`${domain}/api/auth/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'urn:ietf:params:oauth:grant-type:device_code', device_code, client_id: clientId, }), }) if (tokenResponse.ok) { const tokens = await tokenResponse.json() console.log(' Erfolgreich authentifiziert!') return tokens } const error = await tokenResponse.json() if (error.error === 'slow_down') { pollInterval += 5000 // Intervall um 5 Sekunden erhöhen continue } if (error.error === 'authorization_pending') { continue // Weiter pollen } // expired_token, access_denied oder anderer Fehler throw new Error(`Authentifizierung fehlgeschlagen: ${error.error_description}`) } throw new Error('Gerätecode abgelaufen. Bitte versuche es erneut.') }

Verwandte Konzepte

  • OAuth 2.0 & OIDC — Das Autorisierungsframework, das der Device-Flow erweitert
  • CIBA (Backchannel Auth) — Ein weiterer Grant-Typ für entkoppelte Authentifizierung
  • Tokens erklärt — JWT-Struktur, Zugriffstoken und Refresh-Token
  • PKCE-Flow — Der standardmäßige browserbasierte Grant-Typ, den der Device-Flow ergänzt