Skip to Content

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:

DispositivoPerché OAuth Standard Fallisce
Smart TVNessuna tastiera; le tastiere a schermo sono scomode per digitare password
CLI toolNessun browser; interfaccia solo terminale
Console di giocoInput da controller; inserire URL e credenziali è impraticabile
Dispositivi IoTNessun display, o display minimale
Segnaletica digitale / kioskAmbiente 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 email

Passo 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 }
CampoDescrizione
device_codeStringa casuale crittograficamente sicura usata dal client nel polling. Mai mostrata all’utente.
user_codeCodice breve leggibile dall’uomo che l’utente digita nella pagina di verifica.
verification_uriL’URL che l’utente visita per inserire il codice.
verification_uri_completeL’URL completo con il codice utente pre-compilato (per i QR code).
expires_inPer quanto tempo i codici sono validi (secondi). Default: 600 (10 minuti).
intervalIntervallo 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-id

Passo 5: Il Server Risponde in Base all’Azione dell’Utente

RispostaStatus HTTPSignificatoAzione del Client
authorization_pending400L’utente non ha ancora completato l’autenticazioneContinua il polling
slow_down400Il client fa polling troppo frequentementeAumenta l’intervallo di 5 secondi
expired_token400Il device_code è scadutoRiavvia il flusso dal Passo 1
access_denied400L’utente ha esplicitamente negato la richiestaMostra un errore; non riprovare
Successo (access token)200L’utente ha approvato la richiestaMemorizza 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 Y

Questo 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 CodeDevice Code
Lunghezza8 caratteri40+ caratteri
Mostrato all’utenteSìNo
Memorizzato lato serverTesto in chiaroHash SHA-256
ScopoIdentificazione dell’utenteAutenticazione del client nel polling

Confronto con Altri Grant Type

Grant TypeInput RichiestoUtente PresenteBrowser NecessarioAdatto Per
Authorization Code + PKCEBrowser + tastieraSìSì (stesso dispositivo)Web app, mobile app
Device AuthorizationSolo displaySì (su dispositivo secondario)Sì (su dispositivo secondario)Smart TV, CLI, IoT
Client CredentialsNessunoNoNoServer-to-server (M2M)
CIBANessuno (notifica push)Sì (su dispositivo di notifica)NoCall 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