KapfenbergCosa succede se …?Sicurezza e accessoLocale invece che cloud

Blink risponde con 202 — e nessuno riusciva a entrare

L'accesso non è riuscito nella versione che ho provato. Tre giorni dopo il progetto aveva tre nuove versioni. Quello che c'è stato in mezzo non era codice.

Nella prima parte avevo preso un progetto per mezzo addormentato e mi sbagliavo. Il riscontro vero non era «non interessa a nessuno» ma «nessuno riesce a entrare».

Ecco da cosa dipendeva. Era un numero di tre cifre che nessuno interrogava.

La strada documentata non funzionava

L’accesso a Blink richiede un PIN via SMS. L’add-on prevedeva due avvii: al primo si inizia l’accesso e arriva il PIN, al secondo lo si inserisce.

Non ha mai funzionato. A ogni tentativo arrivava l’SMS, e a ogni tentativo compariva poi Login failed.

Il mio primo ritrovamento era autoinflitto: il file delle opzioni aveva fine riga in stile Windows, e per questo lo script di avvio leggeva un carattere di troppo. Un errore imbarazzante, corretto in fretta — e dopo compariva di nuovo lo stesso Login failed.

Il riscontro che sembrava definitivo

Letto nel sorgente: al primo passo l’accesso salva in memoria due valori che servono al secondo. Se il servizio riparte, spariscono.

if not hasattr(self, "_oauth_csrf_token") or not hasattr(self, "_oauth_code_verifier"):
    _LOGGER.error("No OAuth 2FA state found. Start login flow first.")
    return False

Così l’accesso in due avvii è impossibile per costruzione. Non «adesso non va», ma: non può riuscire finché tra i due passi c’è un riavvio.

L’ho preso per il riscontro definitivo. Era solo metà della risposta.

L’osservazione che ha ribaltato tutto

Poi è arrivata la frase che ha cambiato direzione — e non veniva dal sorgente ma dalla casa:

Al secondo tentativo Blink ha mandato lo stesso PIN del primo.

Non è una nota a margine. Non scade il PIN — scade la sessione. Il PIN è legato all’account e resta valido; quello che manca sono i due valori in memoria.

E da lì segue subito la strada più semplice: se il PIN è noto in anticipo, nessuno deve aspettare un secondo avvio.

accesso -> richiesta 2FA -> PIN pronto -> usato subito -> token salvato

Nessuna attesa, nessun secondo avvio. La logica di attesa resta come ripiego per quando il PIN non sia noto in anticipo.

Qui un’osservazione quotidiana ha battuto un’analisi del codice. Avevo letto il sorgente e l’avevo capito bene — e avevo comunque tratto la conclusione sbagliata, perché non sapevo che il PIN resta lo stesso.

E poi il numero

Con una riga diagnostica in più nel registro è emerso il fatto mancante:

oauth_signin HTTP 202
Login failed

La libreria usata in quel punto conosce esattamente due casi:

if   response.status == 412:                      return "2FA_REQUIRED"
elif response.status in [301,302,303,307,308]:    return "SUCCESS"
return None                                       # -> "Login failed"

Ma Blink risponde con 202 Accepted — credenziali accettate, codice 2FA inviato. Quel caso sfugge a entrambi i controlli e viene segnalato come fallimento.

Tutto si spiega a posteriori:

Osservazione Spiegazione
a ogni tentativo arrivava un SMS l’accesso riusciva ogni volta
eppure compariva sempre Login failed il 202 non viene riconosciuto
tre volte lo stesso PIN la stessa richiesta aperta, nulla era bloccato

L’accesso non aveva mai fallito. Era solo letto male.

Un messaggio d’errore che dice «Login failed» mentre l’accesso è riuscito manda ogni utente nella direzione sbagliata. Si controllano password, account, blocchi — e niente di tutto ciò è il problema.

È per questo che nessuno entrava. Non per disinteresse, ma per un controllo di stato che non conosce un terzo caso.

Cosa è successo poi

Ho inviato due contributi. Quello che è successo nelle 72 ore seguenti ha liquidato definitivamente la mia valutazione della prima parte:

30.08. due contributi inviati
31.08. il manutentore integra otto contributi, v0.2.0, controlli automatici, roadmap
31.08. uno dei miei integrato «taken as written»
01.09. revisione dettagliata del secondo, tre punti concreti
02.09. un secondo collaudatore indipendente prova quattro telecamere dal mio ramo
02.09. v0.3.0, v0.4.0, v0.4.1 — nove commit in un giorno

Tre salti di versione e un secondo collaudatore indipendente in tre giorni.

In cosa consisteva la spinta — non nella quantità di codice

È la parte che ritengo più importante. Il mio contributo è stato minore di quanto l’effetto lasci supporre. Non era un grande contributo né una nuova funzione.

Quello che contava era nominare con precisione due errori bloccanti, con il motivo:

  • L’accesso non è riuscito nella versione che ho provato — e perché: HTTP 202 invece di 412, e i valori legati alla sessione.
  • Il flusso video si interrompe prima ancora che la telecamera venga svegliata — e perché: il server di Blink risponde in tre punti in modo diverso da quanto lascia attendere la descrizione del protocollo.

Due diagnosi, ciascuna con estratto di registro e punto nel sorgente. Non «da me non funziona», ma «ecco la riga, ed ecco cosa torna invece».

E quindi?

  1. Un messaggio d’errore sbagliato costa più di nessun messaggio. Login failed su un accesso riuscito ha probabilmente allontanato più persone di qualsiasi funzione mancante.
  2. Leggi il sorgente — ma non fidarti subito della tua conclusione. Avevo letto tutto correttamente ed ero comunque fuori strada, finché non è arrivata un’osservazione quotidiana.
  3. Un’osservazione batte un’analisi quando mostra qualcosa che nel codice non c’è. Che il PIN resti lo stesso non è scritto da nessuna parte. Lo si vede solo guardando due volte.
  4. Una segnalazione precisa è un contributo. Non serve programmare per far avanzare un progetto. Serve saper dire dove esattamente si blocca.
  5. Controlla i valori di ritorno non gestiti. Due casi verificati, tutto il resto considerato errore: è una trappola presente in moltissimo codice.

Nella terza parte si fa scomoda: il manutentore risponde a lungo — e mi corregge. A ragione.