Device Authorization Flow
Il Problema: Dispositivi Senza Tastiera
Il grant type più comune di OAuth 2.0 — Authorization Code con PKCE — presume che il client possa aprire un browser, visualizzare una pagina di login e accettare l’input da tastiera dell’utente. Questa assunzione non regge per un’intera categoria di dispositivi:
| Dispositivo | Perché OAuth Standard Fallisce |
|---|---|
| Smart TV | Nessuna tastiera; le tastiere a schermo sono scomode per digitare password |
| CLI tool | Nessun browser; interfaccia solo terminale |
| Console di gioco | Input da controller; inserire URL e credenziali è impraticabile |
| Dispositivi IoT | Nessun display, o display minimale |
| Segnaletica digitale / kiosk | Ambiente locked-down; nessuna navigazione browser consentita |
| Streaming dongle (Chromecast, Fire Stick) | Solo telecomando; nessuna tastiera |
Questi dispositivi devono autenticare gli utenti, ma non possono ospitare un form di login. L’utente deve autenticarsi altrove — sul suo telefono o laptop — e il dispositivo deve sapere che l’autenticazione è riuscita.
Questo è esattamente ciò che risolve l’OAuth 2.0 Device Authorization Grant (RFC 8628).
Come Funziona il Device Flow
Il Device Authorization Grant introduce un pattern con dispositivo secondario: il dispositivo client mostra un codice breve, l’utente inserisce quel codice su un dispositivo con browser (il suo telefono o laptop), e il dispositivo client fa polling al server di autorizzazione fino a quando l’utente completa l’autenticazione.
Il Flusso Passo per Passo
Passo 1: Il Client Richiede i Codici Dispositivo e Utente
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 emailPasso 2: Il Server Restituisce i Codici
{
"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
}| Campo | Descrizione |
|---|---|
device_code | Stringa casuale crittograficamente sicura usata dal client nel polling. Mai mostrata all’utente. |
user_code | Codice breve leggibile dall’uomo che l’utente digita nella pagina di verifica. |
verification_uri | L’URL che l’utente visita per inserire il codice. |
verification_uri_complete | L’URL completo con il codice utente pre-compilato (per i QR code). |
expires_in | Per quanto tempo i codici sono validi (secondi). Default: 600 (10 minuti). |
interval | Intervallo minimo di polling in secondi. |
Passo 3: L’Utente si Autentica sul Dispositivo Secondario
Il dispositivo client mostra il user_code e il verification_uri all’utente. A seconda delle capacità del dispositivo, potrebbe essere:
- CLI tool: Stampa l’URL e il codice nel terminale
- Smart TV: Mostra un grande QR code insieme al codice testuale
- Dispositivo IoT: Mostra il codice su un display LED
Passo 4: Il Client Fa Polling al Token Endpoint
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-idPasso 5: Il Server Risponde in Base all’Azione dell’Utente
| Risposta | Status HTTP | Significato | Azione del Client |
|---|---|---|---|
authorization_pending | 400 | L’utente non ha ancora completato l’autenticazione | Continua il polling |
slow_down | 400 | Il client fa polling troppo frequentemente | Aumenta l’intervallo di 5 secondi |
expired_token | 400 | Il device_code è scaduto | Riavvia il flusso dal Passo 1 |
access_denied | 400 | L’utente ha esplicitamente negato la richiesta | Mostra un errore; non riprovare |
| Successo (access token) | 200 | L’utente ha approvato la richiesta | Memorizza i token; autenticazione completata |
La risposta slow_down non è solo informativa — è un requisito obbligatorio. Quando un client riceve slow_down, deve aumentare il suo intervallo di polling di almeno 5 secondi.
Design del User Code
Il user_code è l’elemento UX più critico nel Device Flow. Auris genera i codici utente da un alfabeto ristretto di 20 caratteri:
B C D F G H J K M N P Q R T V W X YQuesto esclude tutte le vocali (prevenendo la generazione accidentale di parole offensive) e tutti i caratteri ambigui. Con un codice a 8 caratteri da un alfabeto di 20 caratteri: 20^8 ≈ 25,6 miliardi di codici possibili.
I codici utente devono essere a singolo utilizzo e scadere prontamente. Auris cancella il record DeviceCode non appena l’utente approva o nega la richiesta, o quando il periodo expires_in scade.
Sicurezza del Device Code
Mentre il user_code è breve e leggibile dall’uomo, il device_code è una stringa casuale crittograficamente sicura. Auris memorizza i device code come hash SHA-256, non come testo in chiaro.
| Proprietà | User Code | Device Code |
|---|---|---|
| Lunghezza | 8 caratteri | 40+ caratteri |
| Mostrato all’utente | Sì | No |
| Memorizzato lato server | Testo in chiaro | Hash SHA-256 |
| Scopo | Identificazione dell’utente | Autenticazione del client nel polling |
Confronto con Altri Grant Type
| Grant Type | Input Richiesto | Utente Presente | Browser Necessario | Adatto Per |
|---|---|---|---|---|
| Authorization Code + PKCE | Browser + tastiera | Sì | Sì (stesso dispositivo) | Web app, mobile app |
| Device Authorization | Solo display | Sì (su dispositivo secondario) | Sì (su dispositivo secondario) | Smart TV, CLI, IoT |
| Client Credentials | Nessuno | No | No | Server-to-server (M2M) |
| CIBA | Nessuno (notifica push) | Sì (su dispositivo di notifica) | No | Call center, approvazione pagamenti |
Esempio di Codice: CLI Tool con Device Flow
async function loginWithDeviceFlow(clientId: string, domain: string) {
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, interval, expires_in } =
await deviceResponse.json()
console.log('\n Per accedere, visita:', verification_uri)
console.log(' e inserisci il codice:', user_code)
console.log(`\n Questo codice scade tra ${Math.floor(expires_in / 60)} minuti.\n`)
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(' Autenticato con successo!')
return tokens
}
const error = await tokenResponse.json()
if (error.error === 'slow_down') {
pollInterval += 5000
continue
}
if (error.error === 'authorization_pending') continue
throw new Error(`Autenticazione fallita: ${error.error_description}`)
}
throw new Error('Codice dispositivo scaduto. Riprova.')
}Concetti Correlati
- OAuth 2.0 & OIDC — Il framework di autorizzazione che il Device Flow estende
- CIBA (Backchannel Auth) — Un altro grant type per l’autenticazione disaccoppiata
- Token Spiegati — Struttura JWT, token di accesso e refresh token
- PKCE Flow — Il grant type standard basato su browser che il Device Flow complementa