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ät | Warum Standard-OAuth scheitert |
|---|---|
| Smart-TVs | Keine Tastatur; Bildschirmtastaturen sind für die Passworteingabe mühsam |
| CLI-Tools | Kein Browser; reine Terminal-Schnittstelle |
| Spielkonsolen | Controller-Eingabe; URLs und Anmeldedaten eingeben ist unpraktisch |
| IoT-Geräte | Kein Display überhaupt oder ein minimales Display (z. B. LED-Matrix) |
| Digitale Beschilderung / Kioske | Gesperrte 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 emailSchritt 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
}| Feld | Beschreibung |
|---|---|
device_code | Ein langer, kryptographisch zufälliger String, den der Client beim Polling verwendet. Wird dem Benutzer nie angezeigt. |
user_code | Ein kurzer, menschenlesbarer Code, den der Benutzer auf der Verifizierungsseite eingibt. |
verification_uri | Die URL, die der Benutzer besucht, um den Code einzugeben. Muss kurz und einprägsam sein. |
verification_uri_complete | Die vollständige URL mit vorausgefülltem Benutzercode (für QR-Codes). |
expires_in | Wie lange Geräte- und Benutzercodes gültig sind (Sekunden). Standard: 600 (10 Minuten). |
interval | Minimales 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-idSchritt 5: Server antwortet basierend auf der Benutzeraktion
Der Server antwortet mit einem von mehreren Ergebnissen, abhängig vom aktuellen Status:
| Antwort | HTTP-Status | Bedeutung | Client-Aktion |
|---|---|---|---|
authorization_pending | 400 | Benutzer hat die Authentifizierung noch nicht abgeschlossen | Im konfigurierten Intervall weiter pollen |
slow_down | 400 | Client pollt zu häufig | Polling-Intervall um 5 Sekunden erhöhen |
expired_token | 400 | Der device_code ist abgelaufen (Benutzer hat zu lange gebraucht) | Flow von Schritt 1 neu starten |
access_denied | 400 | Benutzer hat die Autorisierungsanfrage explizit abgelehnt | Fehler dem Benutzer anzeigen; nicht wiederholen |
| Erfolg (Zugriffstoken) | 200 | Benutzer hat die Anfrage genehmigt | Tokens 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:
- Kurz: Einfach auf einer Telefontatstatur einzutippen (8 Zeichen ist der Sweet-Spot)
- Eindeutig: Keine Zeichen, die gleich aussehen (vermeide
0/O,1/I/l,5/S,2/Z) - Groß-/Kleinschreibungsunempfindlich: Benutzer sollten sich keine Gedanken über Groß-/Kleinschreibung machen
- Gruppierbar: Ein Bindestrich oder Leerzeichen in der Mitte hilft der Lesbarkeit (
WDJB-MJHTvsWDJBMJHT)
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 YDies 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.
| Eigenschaft | Benutzercode | Gerätecode |
|---|---|---|
| Länge | 8 Zeichen | 40+ Zeichen |
| Alphabet | 20 Großbuchstaben | Vollständiges URL-sicheres Base64 |
| Entropie | ~34,6 Bit | ~240 Bit |
| Dem Benutzer angezeigt | Ja | Nein |
| Server-seitig gespeichert | Klartext (für Benutzercode-Abgleich) | SHA-256-Hash (wie Passwörter) |
| Zweck | Benutzeridentifikation | Client-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:
- 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.
- 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.
- Kurze Lebensdauer: Das 10-Minuten-Fenster begrenzt das Angriffsfenster.
- Rate-Limiting: Auris begrenzt Gerätecode-Anfragen pro Client, um Massenerzeugungs-Angriffe zu verhindern.
Vergleich mit anderen Grant-Typen
| Grant-Typ | Benötigte Eingabe | Benutzer anwesend | Browser benötigt | Am besten für |
|---|---|---|---|---|
| Authorization Code + PKCE | Browser + Tastatur | Ja | Ja (auf gleichem Gerät) | Web-Apps, mobile Apps |
| Device Authorization | Nur Display | Ja (auf sekundärem Gerät) | Ja (auf sekundärem Gerät) | Smart-TVs, CLI-Tools, IoT |
| Client Credentials | Keine | Nein | Nein | Server-zu-Server (M2M) |
| CIBA | Keine (Push-Benachrichtigung) | Ja (auf Benachrichtigungsgerät) | Nein | Call-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:
| Einstellung | Standard | Beschreibung |
|---|---|---|
| Device-Flow aktivieren | false | Haupt-Toggle |
| Benutzercode-Länge | 8 | Anzahl der Zeichen im Benutzercode |
| Code-Lebensdauer | 600 | Sekunden, bevor Geräte-/Benutzercodes ablaufen |
| Polling-Intervall | 5 | Minimale Sekunden zwischen Token-Endpunkt-Polls |
API-Endpunkte
| Endpunkt | Methode | Beschreibung |
|---|---|---|
/api/oauth/device | POST | Geräte- und Benutzercodes anfordern |
/api/auth/token | POST | Token-Endpunkt (unterstützt device_code Grant-Typ) |
/hosted/device | GET | Benutzerorientierte 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