Skip to Content

CIBA (Client Initiated Backchannel Authentication)

Il Problema: Autenticazione Avviata da Qualcun Altro

I flussi OAuth 2.0 tradizionali assumono che un singolo utente interagisca con un singolo dispositivo. Questo modello funziona per le web app e le mobile app, ma cade quando la persona che richiede l’autenticazione non è la stessa che autentica.

ScenarioPerché OAuth Standard Fallisce
Agente call center che verifica un clienteL’agente non può digitare la password del cliente
Terminale di pagamento in un negozioIl cassiere avvia, il cliente approva sul telefono
Smart speaker per acquistiNessuno schermo per il login
Kiosk check-in sanitarioIl receptionist avvia, il paziente approva sul telefono
Autorizzazione bonifico bancarioIl sistema bancario avvia, il titolare approva da remoto

Cos’è CIBA

CIBA (Client Initiated Backchannel Authentication) è un’estensione OpenID Connect che consente a un’applicazione client di avviare un flusso di autenticazione per un utente noto, dove l’utente autentica su un dispositivo di autenticazione separato (tipicamente il telefono) tramite notifica push, SMS o email.

La distinzione chiave dagli altri flussi OAuth:

  • Authorization Code + PKCE: L’utente guida il flusso dall’inizio alla fine sullo stesso dispositivo.
  • Device Flow: Il client mostra un codice, l’utente visita un URL e lo inserisce. L’utente avvia l’interazione sul dispositivo secondario.
  • CIBA: Il client avvia, il server invia una notifica all’utente. L’utente reagisce solo.

Come Funziona CIBA: Passo per Passo

Passo 1: Il Client Invia una Richiesta di Autenticazione Backchannel

POST /api/oauth/ciba HTTP/1.1 Host: auth.example.com Content-Type: application/x-www-form-urlencoded Authorization: Bearer <client_access_token> [email protected] &scope=openid profile &binding_message=Verifica identità per agente Sara (rif: TX-9821) &requested_expiry=120 &client_notification_token=notification-callback-token-xyz
ParametroObbligatorioDescrizione
login_hintSìIdentifica l’utente — email, numero di telefono o user ID
scopeSìScope OAuth richiesti (deve includere openid)
binding_messageConsigliatoDescrizione leggibile dall’uomo mostrata all’utente sul dispositivo di autenticazione
requested_expiryOpzionaleDurata di validità della richiesta (secondi). Default: 300
client_notification_tokenCondizionaleRichiesto per le modalità ping e push

Passo 2: Il Server Restituisce un ID di Richiesta di Autenticazione

{ "auth_req_id": "ciba_req_1a2b3c4d5e6f7g8h", "expires_in": 120, "interval": 5 }

Passo 3: Il Server Notifica l’Utente

Auris invia una notifica al dispositivo di autenticazione dell’utente tramite uno dei canali configurati:

  • Notifica push: Push nativo per mobile
  • SMS: Messaggio con un link alla pagina di approvazione
  • Email: Email con un link alla pagina di approvazione

La notifica include il binding_message affinché l’utente sappia esattamente cosa sta approvando.

Passo 4: L’Utente Approva o Nega

L’utente vede il binding message e il nome dell’applicazione richiedente sul suo dispositivo di autenticazione. Può approvare o negare.

Passo 5: Il Client Ottiene i Token

Come il client riceve i token dipende dalla modalità di notifica configurata.

Le Tre Modalità di Notifica

Modalità Poll

La modalità più semplice. Il client fa polling al token endpoint all’interval configurato, esattamente come nel Device Flow:

POST /api/auth/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded grant_type=urn:openid:params:grant-type:ciba &auth_req_id=ciba_req_1a2b3c4d5e6f7g8h &client_id=call-center-app &client_secret=client-secret

Adatto per: Integrazioni semplici dove una latenza di 5 secondi è accettabile.

Modalità Ping

Il server invia un HTTP POST all’URL di callback registrato del client quando l’utente risponde. Il callback contiene solo l’auth_req_id — il client deve poi recuperare i token dal token endpoint.

Adatto per: Sistemi di produzione dove il client ha un URL di callback raggiungibile pubblicamente e ha bisogno di latenza inferiore alla modalità poll.

Modalità Push

Il server invia i token direttamente all’URL di callback del client. Il client non chiama mai il token endpoint.

Adatto per: Latenza minima. Tuttavia, i token viaggiano sulla rete verso il callback del client, aumentando la superficie d’attacco.

Confronto Modalità

DimensionePollPingPush
LatenzaAlta (intervallo polling)Bassa (avviata dal server)Minima (token nel callback)
Complessità clientSemplice (loop polling)Media (endpoint callback + fetch token)Media (endpoint callback)
Infrastruttura clientNessuna (solo outbound)Richiede URL callback pubblicoRichiede URL callback pubblico

Auris supporta tutte e tre le modalità. La modalità Poll è predefinita per le nuove applicazioni. Per usare le modalità ping o push, registra un backchannel_client_notification_endpoint nelle impostazioni dell’applicazione.

Il Binding Message

Il binding_message è probabilmente la funzionalità di sicurezza più importante di CIBA. Senza di esso, CIBA è vulnerabile agli attacchi di confused deputy: un attaccante avvia una richiesta CIBA per un utente vittima, e la vittima vede un generico “Approva login?” senza contesto.

Con un binding message, la vittima vede:

“Autorizza bonifico di €5.000 al conto che termina in 7892 (rif: WT-2024-0918)”

Se la vittima non ha avviato un bonifico, sa di dover negare la richiesta.

Il binding_message viene mostrato sul dispositivo dell’utente, che potrebbe essere una notifica push visibile sulla schermata di blocco. Non includere mai password, numeri di carta di credito completi o altre informazioni sensibili nel binding message.

Confronto: CIBA vs Device Flow

DimensioneDevice FlowCIBA
Chi avviaUtente (visita un URL)Server (invia notifica)
Inserimento codiceL’utente digita il codice manualmenteNessun inserimento codice; l’utente approva
Registrazione utente preventivaNon richiestaRichiesta (il server deve sapere come raggiungere l’utente)
Utenti anonimiSupportatiNon supportati
Latenza UXAlta (l’utente deve visitare URL, digitare codice)Bassa (l’utente tocca approva sulla notifica)
Infrastruttura notificaNessunaRichiede capacità push/SMS/email

Esempio di Codice: Flusso Agente Call Center

async function verifyCustomerIdentity( customerEmail: string, agentName: string, referenceNumber: string ) { const cibaResponse = await fetch('https://auth.example.com/api/oauth/ciba', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', Authorization: `Bearer ${agentAccessToken}`, }, body: new URLSearchParams({ login_hint: customerEmail, scope: 'openid profile', binding_message: `Verifica identità da ${agentName} (rif: ${referenceNumber})`, requested_expiry: '120', }), }) const { auth_req_id, interval, expires_in } = await cibaResponse.json() console.log(`Verifica inviata a ${customerEmail}. In attesa di approvazione...`) 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('https://auth.example.com/api/auth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'urn:openid:params:grant-type:ciba', auth_req_id, client_id: 'call-center-app', client_secret: 'client-secret', }), }) if (tokenResponse.ok) { const { id_token } = await tokenResponse.json() console.log('Identità cliente verificata.') return id_token } const error = await tokenResponse.json() if (error.error === 'slow_down') { pollInterval += 5000; continue } if (error.error === 'authorization_pending') continue if (error.error === 'access_denied') { throw new Error('Il cliente ha negato la richiesta di verifica.') } throw new Error(`Verifica fallita: ${error.error_description}`) } throw new Error('Richiesta di verifica scaduta. Il cliente non ha risposto in tempo.') }

Concetti Correlati