---
url: 'https://www.corbado.com/it/blog/errori-webauthn'
title: 'Guida definitiva agli errori WebAuthn in produzione (2026)'
description: 'Scopri cosa significano i comuni errori WebAuthn come NotAllowedError in produzione e come classificarli per tipo di operazione, tempistiche e contesto.'
lang: 'it'
author: 'Vincent Delitz'
date: '2026-07-03T07:08:54.339Z'
lastModified: '2026-07-03T07:10:43.703Z'
keywords: 'errori webauthn, NotAllowedError, AbortError, SecurityError, risoluzione problemi webauthn, classificazione errori passkey, errori conditional create, ASAuthorizationError, codici ASAuthorizationError, androidx.credentials, errori passkey credential manag'
category: 'WebAuthn Know-How'
---

# Guida definitiva agli errori WebAuthn in produzione (2026)

## 1. Introduzione

In produzione, gli errori WebAuthn sono fonte di confusione
perché i browser espongono un set ridotto di nomi `DOMException` (come `NotAllowedError`) che
possono rappresentare molteplici cause di fondo. Oltre a ciò, la stragrande maggioranza degli "errori" -
spesso superiore al 95% nelle implementazioni
su larga scala ottimizzate - rappresenta in realtà
un **comportamento previsto** (l'utente ha interrotto la richiesta passkey del sistema operativo).

> Importante: per motivi di privacy, i browser non distinguono se l'utente ha
> annullato attivamente l'operazione o se non esisteva alcuna passkey. Tuttavia, in alcune situazioni e con abbastanza
> contesto, sia sul web che sulle piattaforme native, alcuni di questi casi possono essere
> differenziati utilizzando segnali come le tempistiche.

Se desideri le definizioni canoniche per questi nomi, inizia da
[MDN `DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException). Per
le condizioni specifiche di WebAuthn che portano a queste eccezioni (e che i browser sono tenuti
a far rispettare), consulta la [Specifica W3C Web Authentication](https://www.w3.org/TR/webauthn-3/).

Se tratti tutti gli errori come "bug", farai le cose sbagliate:

- inquinerei le tue metriche di errore con normali annullamenti
- ti perderai regressioni reali nascoste all'interno della massa di `NotAllowedError`
- pubblicherai un'interfaccia utente (UI) che confonde gli utenti anziché aiutarli a riprendersi

In questo articolo rispondiamo a:

- Cosa significano solitamente i nomi degli errori WebAuthn più comuni nel traffico reale?
- Come si disambigua `NotAllowedError` in bucket azionabili (annullamento vs timeout vs
  disponibilità)?
- Perché lo stesso errore significa cose diverse a seconda dell'operazione (login con Conditional UI
  vs login modale vs creazione di passkey vs
  creazione condizionale)?
- Quale contesto minimo dovresti acquisire in modo che "ha fallito" diventi un problema riproducibile?

## Key Facts

- `NotAllowedError` è un **segnale di superficie**, non una causa principale. Può significare annullamento,
  timeout, "nessuna credenziale locale" o attivazione utente mancante a seconda del contesto.
- **Il tipo di operazione cambia il significato.** Lo stesso `NotAllowedError` significa cose
  diverse durante il login con Conditional UI, il login modale, la creazione manuale di passkey, la creazione condizionale
  e le aggiunte attivate automaticamente.
- **Le tempistiche dall'inizio dell'operazione** sono il segnale più sottovalutato: immediato (`<1s`),
  annullamento dell'utente (1-15s) e timeout (30s+) sono categorie fondamentalmente diverse.
- `AbortError` è solitamente un problema di ciclo di vita/concorrenza (navigazione, re-render, richieste
  multiple in corso).
- `SecurityError` è quasi sempre legato a configurazione/contesto ed è raro nelle implementazioni di produzione
  mature.
- I "nomi degli errori del browser" e i "rifiuti di verifica del server" sono livelli diversi. Tieni traccia
  dei rifiuti del server come codici espliciti, non come errori generici del client.
- **I nomi degli errori non elaborati non sono utilizzabili da soli.** Acquisisci sempre il tipo di operazione, le tempistiche e
  il contesto della piattaforma insieme a `error.name` in modo da poter classificare gli errori in bucket che puoi
  effettivamente correggere.
- **"Ambiente" è più che semplice Browser + OS.** Per comprendere veramente gli errori, devi
  tracciare l'intera combinazione: versione del sistema operativo, client (versione di browser/app), impostazioni dell'autenticatore
  (es. stato iCloud/GPM) e il modello hardware specifico.
- **Gli errori di login sono P1, gli errori di creazione sono P2.** Mentre gli errori di creazione (P2) spesso
  hanno volumi maggiori a causa degli abbandoni degli utenti, gli errori di login (P1) bloccano l'accesso e richiedono
  avvisi immediati.

## 2. Un cheat sheet per la produzione

Se hai solo bisogno di una mappatura rapida per sbloccare il debugging, inizia con questa tabella. È sbilanciata
verso ciò che i team vedono effettivamente nelle dashboard e nei ticket di supporto.

| **`error.name`**    | **Cosa significa di solito in produzione**                                                                                                        | **Cosa controllare per confermare**                                                                            | **Prima azione (UX + ingegneria)**                                                                 |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `NotAllowedError`   | Foglio ignorato dall'utente, timeout o mancata disponibilità raggruppati in un unico bucket. Questo è il bucket di errori più grande in produzione. | time-to-error, se è apparsa l'interfaccia utente QR/ibrida, se la cerimonia è iniziata da un'azione utente reale | Tratta come previsto: ripristina UI + mostra fallback                                              |
| `AbortError`        | La tua app (o il browser) ha interrotto la cerimonia                                                                                              | navigazione/re-render durante la cerimonia; chiamate WebAuthn concorrenti; `AbortController.abort()`           | Imponi una sola richiesta in corso; impedisci cambi di route; gestisci l'interruzione come normale flusso di controllo |
| `SecurityError`     | Contesto/policy non consentito                                                                                                                    | origine + strategia RP ID; iframe/embedding; HTTPS; feature policy                                             | Correggi configurazione RP ID/origine; convalida le policy di embedding; assicurati che il contesto sia sicuro |
| `InvalidStateError` | Mancata corrispondenza dello stato (spesso registrazione duplicata)                                                                               | registrazione vs login; `excludeCredentials`; credenziale esistente sull'autenticatore                         | Tratta come "già registrato"; adatta il percorso UX; correggi la generazione delle opzioni         |
| `ConstraintError`   | I requisiti non possono essere soddisfatti                                                                                                        | `authenticatorAttachment`, `userVerification`, requisiti delle resident key                                    | Rilassa i vincoli o fornisci un percorso/fallback alternativo. Esempio: blocco schermo mancante su Android |
| `DataError`         | Gli input sono malformati/incoerenti                                                                                                              | codifica base64url; formati di id/challenge/user handle                                                        | Correggi codifica/serializzazione; aggiungi convalida nella generazione delle opzioni              |
| `NotSupportedError` | La piattaforma/il browser non supporta ciò che hai richiesto                                                                                      | versione di OS/browser; presupposti del rilevamento delle funzionalità                                         | Applica subito un fallback; registra il segmento; evita di mostrare CTA per passkey in ambienti non supportati |
| `UnknownError`      | Piattaforma/autenticatore non riuscito in modo generico                                                                                           | picchi dopo aggiornamenti OS; build del dispositivo; problemi del provider del credential manager              | UX compatibile con i tentativi (retry); acquisisci numeri di build; indaga sui picchi del segmento |

Una cosa facile da trascurare: lo stesso `error.name` può significare cose molto diverse
a seconda del **tipo di operazione**. Tieni presente il contesto dell'operazione mentre leggi le
sezioni seguenti. In pratica, gli errori di creazione di passkey
(registrazione) superano in genere di gran lunga gli errori di login: la tabella precedente
si applica a entrambi, ma la creazione è dove risiede la maggior parte del volume.

Successivamente, approfondiremo `NotAllowedError` perché è quello che vedrai di più e
quello che i team fraintendono più spesso.

## 3. NotAllowedError: l'operazione è andata in timeout o non era consentita

`NotAllowedError` spesso sembra che "le passkey abbiano fallito", ma di solito è la piattaforma
che ti dice che l'utente non ha completato l'interfaccia utente del sistema operativo. La chiave è suddividerlo in bucket su cui puoi
agire.

**Cosa vedrai nella console del browser:**

| **Origine**    | **Messaggio di errore**                                                                                                                              |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Chrome, Edge   | `NotAllowedError: The operation either timed out or was not allowed. See: https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client.`     |
| Safari, WebKit | `NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.` |
| Safari, WebKit | `NotAllowedError: This request has been cancelled by the user.`                                                                                      |
| Chrome, Edge   | `NotAllowedError: The operation is not allowed at this time because the page does not have focus.`                                                   |
| Safari, WebKit | `NotAllowedError: The document is not focused.`                                                                                                      |
| Firefox        | `NotAllowedError: Operation failed.`                                                                                                                 |

Tutti questi emergono come `error.name === "NotAllowedError"`. Il `error.message` differisce in base
al motore del browser e alla causa sottostante, ma il risultato è lo stesso: la cerimonia non
è stata completata.

Ciò si applica sia al **login che alla creazione della passkey**. Durante il login (Conditional UI, modale
con o senza allowList), `NotAllowedError` in genere significa che l'utente non ha completato la
cerimonia. Durante la creazione della passkey, lo stesso errore
emerge per motivi diversi: l'utente ha chiuso la finestra di dialogo di creazione (la creazione condizionale
non ha funzionato, o la pagina ha perso il focus durante un'aggiunta attivata automaticamente. Il tipo di operazione
cambia il significato dell'errore e cosa dovresti fare al riguardo.

**Le tempistiche sono spesso un segnale sottovalutato.** Un errore dopo meno di un secondo da un clic
di solito è un rifiuto immediato (l'ambiente non può farlo, il documento non è focalizzato, manca la
capacità). Un errore dopo pochi secondi è un annullamento dell'utente (ha visto la finestra di dialogo e
ha deciso di non procedere). Un errore dopo oltre 30 secondi è un timeout. Sulle piattaforme native,
le tempistiche sono particolarmente importanti: i round-trip dell'autenticatore,
i prompt biometrici e gli handoff del credential manager hanno tutti durate caratteristiche che
ti aiutano a separare "non ha funzionato" da "l'utente si è allontanato". Non riesci ancora a distinguere
facilmente se esisteva una passkey.

### 3.1 Disambiguare con il contesto

Non hai bisogno di un segnale perfetto. Hai bisogno di un contesto sufficiente per evitare di trattare ogni
`NotAllowedError` allo stesso modo. iOS/Safari riceve
un'attenzione specifica di seguito perché ha vincoli unici (requisiti di attivazione dell'utente
nelle versioni precedenti), ma nel volume di errori puro, Windows e i browser Chromium spesso
generano più `NotAllowedError` di qualsiasi altra piattaforma. Questi segnali ti portano spesso all'80%
del risultato:

| **Segnale**                                                  | **Significato probabile**                                                                                                                                                                                                                                                                            | **Cosa fare dopo**                                                                                                                                                                            |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Errore immediato (`<1s`)                                     | Rifiuto dell'ambiente: nessuna capacità, documento non focalizzato, superficie di creazione condizionale non disponibile                                                                                                                                                                             | Controlla il rilevamento delle funzionalità; assicurati che il documento sia in primo piano; verifica che l'operazione sia supportata su questa piattaforma                                   |
| Annullamento rapido (1-3s)                                   | Prompt a sorpresa / nessun contesto                                                                                                                                                                                                                                                                  | Cambia la tempistica del prompt; aggiungi un tempo di ricarica (cooldown) dopo l'annullamento                                                                                                 |
| Annullamento con tempistiche dell'utente (3-15s)             | L'utente ha visto la finestra di dialogo e ha scelto di non procedere                                                                                                                                                                                                                                | UX prevista; ripristina UI + mostra fallback                                                                                                                                                  |
| Timeout (30s+)                                               | La cerimonia è andata in timeout senza l'intervento dell'utente                                                                                                                                                                                                                                      | Inserisci nel bucket "non completato"; valuta se il prompt sia stato notato                                                                                                                   |
| Appare UI QR/ibrida prima del fallimento                     | Nessuna credenziale locale disponibile su questo dispositivo. Nota: rilevare in modo affidabile le decisioni del codice QR prima che si verifichino richiede un livello di [passkey intelligence](https://docs.corbado.com/corbado-connect/features/passkey-intelligence) che sappia se esiste una credenziale utilizzabile sul dispositivo attuale. | Limita le offerte di passkey; rendi esplicito "Usa telefono"; riduci le sorprese con i QR                                                                                                     |
| Concentrato su iOS/Safari e attivato senza un clic/tocco     | Attivazione utente mancante                                                                                                                                                                                                                                                                          | Avvia la cerimonia da un vero gesto dell'utente                                                                                                                                               |
| Durante conditional create o aggiunta attivata automaticamente | Autofill non disponibile, credenziale già esistente o pagina che ha perso il focus. Gli errori di conditional create possono apparire all'improvviso e ad alto volume quando la funzione viene lanciata, rendendo questa una delle fonti di errore più grandi durante la notte.                    | Vedi conditional create; controlla lo stato di visibilità del documento; usa `getClientCapabilities()` per verificare il supporto a `conditionalCreate` prima di tentare la chiamata          |

Questo è anche il motivo per cui `NotAllowedError` dovrebbe essere raramente visibile all'utente. Non è un messaggio su cui
l'utente può agire.

La classificazione del contesto è anche il punto in cui i tassi di successo si dividono più nettamente. L'
[Analisi del tasso di successo dell'autenticazione tramite passkey del Corbado Passkey Benchmark 2026](https://www.corbado.com/passkey-benchmark-2026/passkey-authentication-success-rate)
misura il completamento del primo trimestre 2026 al 55-95% per i flussi identifier-first su dispositivi sconosciuti rispetto
al 95-99% per i ritorni su dispositivi noti in tutte le
implementazioni B2C su larga scala.
L'identifier-first web su iOS raggiunge un completamento dell'85-95%
(% CDA 0–5%), web su Android al 70–85% (% CDA 5–10%) e web su macOS al 70–85% (% CDA 10–15%),
mentre il web su Windows si attesta al 45-60% di completamento identifier-first con % CDA al 55-65% su
Windows 11 e 40-55% su Windows 10. Leggere
il volume di `NotAllowedError` senza separare i contesti di dispositivi noti rispetto a quelli sconosciuti confonde
due regimi di successo fondamentalmente diversi.

Una sfumatura che conta in produzione: alcuni user agent potrebbero mostrare i timeout come
`TimeoutError`, ma molti team vedono ancora i timeout raggruppati in `NotAllowedError` nelle
dashboard. In ogni caso, tratta i timeout come "la cerimonia non è stata completata" e classificali usando
tempistiche e contesto.

### 3.2 Gestione UX: rendi l'annullamento un'uscita normale

Quando il foglio del sistema operativo viene ignorato o va in timeout, l'interfaccia utente dovrebbe riprendersi immediatamente e
reagire in modo aggraziato. Un insieme pratico di regole:

- ripristina la UI di login (nessun caricatore che continua a girare)
- mantieni lo stato dell'identificatore (non forzare il reinserimento)
- mostra un fallback visibile nella stessa schermata
- evita banner spaventosi per risultati previsti

Oltre le basi:

- Tratta la prima interruzione come normale e offri un nuovo tentativo con una spiegazione chiara. Solo dopo un
  secondo annullamento dovresti suggerire opzioni di fallback.
- Consenti fino a tre richieste di creazione prima di un cooldown, in modo che gli utenti sorpresi abbiano una seconda
  possibilità.
- Dividi i conteggi degli errori tra creazione e login in modo da confrontare elementi simili.
- Segmenta i tassi di errore in base a sistema operativo, browser e dispositivo in modo da poter individuare dove risiede effettivamente
  l'attrito.

Se i tuoi "annullamenti" sono veramente alti, il passo successivo è correggere le cause principali alla loro base:
tempistiche del prompt, sorprese sui codici QR e bassa disponibilità.

### 3.3 Correzioni ingegneristiche che riducono NotAllowedError

Inizia con le modifiche che spostano rapidamente le metriche:

- Correggi le tempistiche del prompt e l'attivazione dell'utente (soprattutto su
  iOS/Safari): eventi attivati dall'utente WebAuthn su Safari.
- Riduci le sorprese con i QR/ibridi: perché i flussi passkey mostrano i codici QR e gli utenti abbandonano.
- Gestisci le offerte in modo che le passkey vengano mostrate quando è probabile che abbiano successo: offri le passkey solo quando
  hanno un'alta probabilità di successo.
- Utilizza modelli che evitano di inviare prompt quando non esiste alcuna credenziale locale:
  WebAuthn immediate mediation.
- **Gestisci le instabilità di rete:** Gli errori di rete durante la verifica si manifestano spesso come
  guasti generici lato client. Implementa una coda offline per i log della tua telemetria, così
  non perdi i dati sugli errori quando la connessione dell'utente cade durante la cerimonia.
- **Limita le cerimonie con le API di rilevamento** in modo che gli errori di ambiente non gonfino il tuo
  bucket `NotAllowedError`. Inizia con
  `isUVPAA()` come controllo più
  basilare, quindi usa `getClientCapabilities()` per
  controlli più precisi (creazione condizionale, get condizionale, trasporto ibrido,
  autenticatore di piattaforma). Tieni presente che le
  API di rilevamento possono interrompersi con gli aggiornamenti del sistema operativo: iOS 26.2 includeva un bug WebKit
  in cui `isUVPAA()` restituiva `false` su tutti i browser basati su WKWebView
  anche se le passkey funzionavano correttamente, causando improvvisi picchi di `NotAllowedError` per
  il 10-25% degli utenti iOS.

### 3.4 Nota sull'evoluzione dei nomi degli errori

I nomi degli errori sono un bersaglio mobile. Ci sono proposte in corso per aggiungere errori WebAuthn più granulari
(ad esempio, per separare "nessuna credenziale disponibile" da "l'utente ha annullato"). A partire da
febbraio 2026, ciò non è implementato in nessun browser, quindi vale ancora la pena creare i
propri bucket di motivazioni in base al contesto e alle tempistiche. Se desideri seguire questo lavoro, vedi
[WebAuthn issue #2062](https://github.com/w3c/webauthn/issues/2062) e l'
[Spiegazione dei nuovi codici di errore](https://github.com/w3c/webauthn/wiki/Explainer%3A-New-Error-Codes-%282024-Edition%29).

I restanti nomi degli errori sono meno frequenti ma vale comunque la pena capirli quando
appaiono.

## 4. AbortError: l'operazione è stata interrotta

`AbortError` è raro in volume rispetto a `NotAllowedError`, ma quando appare è
altamente diagnostico: di solito significa che la cerimonia non è terminata perché la tua app
ha invalidato la richiesta (si è verificata una navigazione, lo stato è cambiato o è iniziata una seconda richiesta).

**Cosa vedrai nella console del browser:**

| **Origine**    | **Messaggio di errore**                                   |
| -------------- | --------------------------------------------------------- |
| Chrome, Edge   | `AbortError: The operation was aborted.`                  |
| Chrome, Edge   | `AbortError: Aborted by AbortSignal.`                     |
| Firefox        | `AbortError: signal is aborted without reason`            |
| Firefox        | `AbortError: Operation timed out.`                        |
| Safari, WebKit | `AbortError: The user aborted a request.`                 |
| Chrome         | `AbortError: CredentialContainer request is not allowed.` |

Le cause comuni di produzione includono:

- più chiamate WebAuthn simultanee (due prompt in competizione)
- cambio di rotta/re-render durante una cerimonia in corso
- chiamata a `AbortController.abort()` durante tentativi o pulizia dello stato

Per risolverlo, concentrati sul rendere la cerimonia una "sezione critica":

- consenti solo una richiesta in corso alla volta
- blocca la navigazione durante la cerimonia (o annulla in modo pulito e ripristina l'interfaccia utente)
- tratta l'interruzione come previsto dal flusso di controllo: mostra un pulsante di riprova e un metodo di fallback

Se noti `AbortError` concentrato in superfici integrate o app multi-dominio, il bucket successivo
da controllare è `SecurityError`.

## 5. SecurityError: WebAuthn non è supportato su siti con errori del certificato TLS

`SecurityError` è il browser che ti dice: "questo contesto non è autorizzato a fare ciò che hai
chiesto." In pratica è quasi sempre configurazione, non comportamento dell'utente. In implementazioni
di produzione mature, `SecurityError` è raro perché questi problemi vengono in genere individuati
durante i test di integrazione. Se compare in produzione, di solito significa che un nuovo dominio,
contesto di integrazione o target di distribuzione è stato aggiunto senza un'adeguata configurazione WebAuthn.

Le cause più comuni includono:

- Mancata corrispondenza di RP ID / origine (configurazioni multi-dominio)
- Restrizioni sull'incorporamento cross-origin (iframe)
- Contesto non sicuro (non HTTPS) o policy/autorizzazioni bloccate
- `.well-known/webauthn` o `.well-known/assetlinks.json` mal configurati, mancanti o
  temporaneamente non disponibili. Problemi di rete durante la finestra critica in cui il browser
  recupera questi file causeranno errori. Un punto cieco comune: se la tua homepage è inattiva
  per manutenzione, anche i file well-known sono offline, interrompendo le cerimonie passkey
  tra tutti i relying party che ne dipendono.

**Cosa vedrai nella console del browser:**

| **Origine**     | **Messaggio di errore**                                                                                                          |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Chrome, Edge    | `SecurityError: WebAuthn is not supported on sites with TLS certificate errors.`                                                 |
| Qualsiasi browser| `SecurityError: The relying party ID is not a registrable domain suffix of, nor equal to the current origin's effective domain.` |
| Chrome (iframe) | `SecurityError: The 'publickey-credentials-create' feature is not enabled in this document.`                                     |

In produzione, il `SecurityError` è raro: quasi sempre vengono rilevati durante i test
di integrazione. Quando compaiono, l'errore del certificato TLS è il sopravvissuto più comune.

Il ciclo di debug più veloce è:

- registra gli input di origine e RP ID che hai usato
- riproduci nello stesso contesto (top-level vs iframe,
  dominio prod vs staging)
- se incorpori il login, verifica che la policy delle autorizzazioni sia configurata (ad esempio
  `publickey-credentials-create` / `publickey-credentials-get`):
  [MDN Permissions-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy/publickey-credentials-create)
- verifica la strategia del tuo dominio
- se utilizzi iframe, convalida le feature policy

Una volta gestito `SecurityError`, il prossimo bucket da prendere sul serio è il set di errori
che spesso indicano bug di implementazione: `InvalidStateError`, `ConstraintError` e
`DataError`.

## 6. InvalidStateError, ConstraintError, DataError: tratta come bug di implementazione

Questi errori dovrebbero essere rari in un'implementazione passkey matura. Quando compaiono, in genere
indicano che la generazione delle opzioni è errata per un segmento o che il flusso è nello
stato sbagliato.

### 6.1 InvalidStateError: "credenziali già registrate con il relying party"

**Cosa vedrai nella console del browser:**

| **Origine**    | **Messaggio di errore**                                                                                                                              |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Safari, WebKit | `InvalidStateError: The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.` |
| Chrome, Edge   | `InvalidStateError: At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.`                 |
| Chrome, Edge   | `InvalidStateError: A request is already pending.`                                                                                                   |
| Firefox        | `InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable`                                                       |

Significati tipici:

- registrazione: hai tentato di creare una credenziale che esiste già (duplicata)
- login: meno comune; spesso significa che il flusso/stato è incoerente per la piattaforma

Gestione pratica:

- se avviene nella registrazione manuale, trattalo come "già registrato" e indirizzalo
  di conseguenza
- assicurati che `excludeCredentials` elenchi tutti gli ID delle credenziali esistenti per l'utente, così
  l'autenticatore può rilevare i duplicati
- durante il conditional create, `InvalidStateError` è
  previsto e dovrebbe essere ignorato silenziosamente: significa che esiste già una passkey nel
  provider. Lo stesso vale per `NotAllowedError` e `AbortError` durante il
  conditional create (vedi
  [conditional create on Chrome](https://developer.chrome.com/docs/identity/webauthn-conditional-create))

### 6.2 ConstraintError

Significato tipico: l'autenticatore non può soddisfare i vincoli
richiesti.

Cause scatenanti comuni:

- blocco schermo del dispositivo mancante (soprattutto su Android):
  la piattaforma richiede la verifica biometrica o del PIN ma il dispositivo non ha una schermata di blocco
  configurata
- requisiti di `authenticatorAttachment` o
  resident key troppo severi
- requisiti rigidi di `userVerification` in segmenti in cui non sono disponibili

Correzione: allenta i vincoli (dove accettabile) o fornisci un percorso alternativo. Per il blocco
schermo mancante, considera di rilevare questa condizione e di guidare gli utenti piuttosto che fallire
in silenzio (Android).

### 6.3 DataError

Significato tipico: gli input sono malformati o incoerenti.

Cause scatenanti comuni:

- errori di codifica (base64 vs base64url)
- id di credenziali non valide / formattazione delle challenge

Correzione: convalida e normalizza gli input al confine in cui generi le opzioni WebAuthn. In
pratica, `DataError` è di fatto assente nei sistemi di produzione maturi: se la generazione delle
tue opzioni è testata, non la vedrai nelle dashboard.

Se questi errori sono sotto controllo, la domanda successiva riguarda la copertura: gli utenti falliscono
perché l'ambiente non può eseguire WebAuthn nel modo in cui ti aspetti?

## 7. NotSupportedError: lo user agent non supporta le credenziali a chiave pubblica

`NotSupportedError` è un segnale di copertura, non un segnale di affidabilità. Di solito significa che un
segmento non può eseguire ciò che hai richiesto (sistema operativo/browser troppo vecchio, capacità mancante, funzionalità non
abilitata).

**Cosa vedrai nella console del browser:**

| **Origine**           | **Messaggio di errore**                                                                        |
| --------------------- | ---------------------------------------------------------------------------------------------- |
| Chrome, Edge          | `NotSupportedError: The user agent does not support public key credentials.`                   |
| Firefox               | `NotSupportedError: Resident credentials or empty 'allowCredentials' lists are not supported.` |
| Chrome, Edge, Firefox | `TypeError: PublicKeyCredential.parseCreationOptionsFromJSON is not a function`                |
| Chrome, Edge, Firefox | `TypeError: PublicKeyCredential.parseRequestOptionsFromJSON is not a function`                 |
| Chrome, Edge, Firefox | `TypeError: credential.toJSON is not a function`                                               |
| Safari                | `TypeError: Can only call PublicKeyCredential.toJSON on instances of PublicKeyCredential`      |

Le prime due sono vere e proprie `NotSupportedError` DOMException. Le voci `TypeError` sono
tecnicamente un tipo di eccezione diverso ma rappresentano la stessa classe di problemi: il
browser o l'ambiente non supporta ciò che hai richiesto. La famiglia `TypeError` legata alla
serializzazione JSON è molto più comune nella pratica rispetto all'eccezione
`NotSupportedError` vera e propria (vedi sotto).

Le cause comuni includono:

- browser/versioni del sistema operativo precedenti che non supportano le funzionalità di base di WebAuthn
- richiesta di funzionalità WebAuthn non disponibili su quella piattaforma
- tentativo di eseguire flussi specifici della piattaforma su dispositivi non supportati

**La famiglia della serializzazione JSON è la più grande fonte di fallimenti della classe `NotSupportedError`
in produzione.** Tecnicamente, questi emergono come `TypeError` (metodo mancante), non
come `DOMException`, ma è qui che li incontrerai. Due cause alla radice distinte:

1. **Il browser non supporta i metodi di serializzazione JSON di WebAuthn.** Il browser ha
   `navigator.credentials` ma non `PublicKeyCredential.parseCreationOptionsFromJSON` /
   `parseRequestOptionsFromJSON`. Ciò rappresenta circa il 90% di questa famiglia di errori,
   concentrata nelle versioni meno recenti di Safari e Chrome. Se la tua libreria client dipende da
   questi metodi senza un fallback, ciò produce un volume di errori significativo.
2. **Le estensioni dei gestori di password interrompono `.toJSON()`.** Le estensioni come
   Bitwarden,
   LastPass o
   1Password possono intercettare la
   cerimonia e restituire un oggetto che sembra una credenziale ma non è un'istanza
   `PublicKeyCredential` reale. Chiamare `.toJSON()` su di esso genera un'eccezione, restituisce
   undefined o l'oggetto è completamente `null`. Questo rappresenta circa il 10% della famiglia, ma
   è particolarmente confuso da eseguire il debug perché i messaggi di errore differiscono in base al browser (Safari:
   "Can only call on instances of PublicKeyCredential"; Firefox: "does not implement
   interface PublicKeyCredential").

La gestione dovrebbe essere diretta e veloce:

- ritorna immediatamente alla password/OTP di fallback
- registra il segmento in modo da poter quantificare i gap di copertura
- evita di mostrare le CTA delle passkey su segmenti che falliranno costantemente

Se la copertura sembra a posto, ma i fallimenti si verificano ancora in segmenti specifici, potresti avere a
che fare con problemi a livello di piattaforma visualizzati come `UnknownError`.

## 8. UnknownError: Si è verificato un errore sconosciuto durante la comunicazione con il gestore delle credenziali

`UnknownError` è un errore generico per i guasti dell'autenticatore/del sistema operativo che non si mappano in modo
pulito nelle altre categorie. È spesso temporaneo, ma può anche verificarsi con dei picchi dopo gli aggiornamenti del sistema operativo.

**Cosa vedrai nella console del browser:**

| **Origine**        | **Messaggio di errore**                                                                                                                 |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| Chrome (Android)   | `UnknownError: An unknown error occurred while talking to the credential manager.`                                                      |
| Qualsiasi browser  | `UnknownError: The operation failed for an unknown transient reason.`                                                                   |
| Qualsiasi browser  | `UnknownError: Either the device has received unexpected request data, or the device has been reconfigured since the request was made.` |
| Qualsiasi browser  | `UnknownError: Something went wrong.`                                                                                                   |
| Chrome (LastPass)  | `TypeError: Cannot use 'in' operator to search for 'type' in null`                                                                      |
| Safari (LastPass)  | `TypeError: null is not an Object. (evaluating 'key in input')`                                                                         |
| Chrome (Bitwarden) | `FallbackRequested`                                                                                                                     |

Gestione pratica:

- usa un'UX che supporti i nuovi tentativi (non dare la colpa all'utente)
- acquisisci i numeri di build del sistema operativo e il contesto del credential-manager/provider ove possibile
- osserva i picchi specifici del segmento dopo gli aggiornamenti
  del sistema operativo

Una fonte di nicchia di errori che non si adatta bene a nessuna categoria `DOMException`:
**estensioni del browser del gestore di password** (come
Bitwarden,
LastPass,
1Password e altri) possono intercettare
le chiamate all'API WebAuthn e restituire risposte non standard. Sebbene di volume ridotto rispetto
agli annullamenti degli utenti, vale la pena monitorarli perché influenzano sistematicamente segmenti di
utenti specifici e i sintomi sono confusi: metodi mancanti sull'oggetto credenziale
restituito, tipi di errore imprevisti o risposte malformate che non corrispondono a nessun errore WebAuthn
documentato. Questi emergono spesso come `UnknownError` o come eccezioni non classificate. Se
vedi picchi di errori concentrati in browser specifici senza alcuna spiegazione a livello di sistema operativo, controlla
se è coinvolta un'estensione per la gestione delle credenziali.

Finora abbiamo coperto i nomi degli errori dei browser web. Ma se rilasci anche app native, il panorama
degli errori è diverso e, per certi versi, nettamente migliore.

## 9. Una parola sulle app native (iOS e Android)

Tutto quanto sopra riguarda i browser web. Le app native - iOS con il framework ASAuthorization,
Android con Credential Manager - condividono
le stesse categorie fondamentali di errori ma differiscono in modo importante:

1. **"Nessuna credenziale" è un segnale distinto.** Sul web, i browser raggruppano "nessuna credenziale
   disponibile" e "utente annullato" nello stesso `NotAllowedError` per motivi di privacy. Sulle versioni native,
   utilizzare `preferImmediatelyAvailableCredentials` su iOS (`ASAuthorizationController`) o
   `setPreferImmediatelyAvailableCredentials(true)` su
   Android (`GetCredentialRequest`) indica al sistema operativo
   di presentare solo le credenziali già presenti sul dispositivo e di fallire immediatamente se non ne esistono.
   Ciò fornisce un segnale pulito di "nessuna credenziale" che il web non può fornire.

2. **Lo stato del provider di credenziali è visibile.** Sulle piattaforme native, in alcune condizioni, puoi
   sapere quando nessun provider di credenziali (Google Password Manager,
   iCloud Keychain,
   1Password, ecc.) è installato,
   configurato o impostato come predefinito e reagire a ciò. Sul web, queste
   informazioni sono nascoste dietro messaggi `NotAllowedError` opachi.

3. **I messaggi di errore sono più specifici.** Poiché l'utente ha installato l'app - e
   di conseguenza ha stabilito un rapporto di fiducia con il
   relying party - il sistema operativo fornisce dettagli diagnostici maggiori. Le
   considerazioni sulla privacy che costringono i browser web a essere vaghi non si applicano allo stesso
   modo quando l'app è già sul dispositivo. iOS restituisce messaggi localizzati nella lingua del
   dispositivo dell'utente. Android restituisce tipi di errore strutturati con catene di cause. Questo rende
   più semplice il debugging ma significa che la gestione degli errori deve tenere conto della localizzazione e
   dei formati di errore specifici della piattaforma.

### 9.1 iOS (Framework ASAuthorization)

iOS espone gli errori delle passkey tramite il framework ASAuthorization. Tutti gli errori arrivano nel
callback del delegato `authorizationController(controller:didCompleteWithError:)` come
oggetti `NSError`.

**Classifica per dominio + codice, non per stringa di messaggio.** Il dominio di errore primario è
`com.apple.AuthenticationServices.AuthorizationError`
(`ASAuthorizationError.errorDomain`). Esegui il cast dell'errore con `let nsError = error as NSError`
e confrontalo su `.domain` e `.code`. Non effettuare mai il match su `.localizedDescription` in produzione -
I messaggi Apple sono localizzati in più di 30 lingue e possono cambiare a seconda delle versioni del sistema operativo. Le
stringhe di messaggio elencate di seguito sono utili per riconoscere gli errori nei log, ma non sono
criteri di classificazione.

**Codici ASAuthorizationError pubblici:**

| **Codice** | **Nome**                                | **Da**    | **Cosa significa**                                                                                                                                                                                                                                                                                                     |
| -------- | --------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1000     | `Unknown`                               | iOS 13    | Non dovrebbe apparire in produzione. Catch-all generico.                                                                                                                                                                                                                                                               |
| 1001     | `Canceled`                              | iOS 13    | L'utente ha chiuso il foglio delle passkey. L'errore più comune in assoluto - l'equivalente di `NotAllowedError`. Segnale pulito con `userInfo` vuoto e nessun errore di fondo.                                                                                                                                        |
| 1002     | `InvalidResponse`                       | iOS 13    | Corruzione a livello di framework. Raro in pratica.                                                                                                                                                                                                                                                                    |
| 1003     | `NotHandled`                            | iOS 13    | Nessun provider ha gestito la richiesta. Controlla gli entitlement e la configurazione del provider di credenziali.                                                                                                                                                                                                    |
| 1004     | `Failed`                                | iOS 13    | Fallimento generico. Il `localizedDescription` contiene il motivo reale (ad es. "Application with identifier X is not associated with domain Y"). `userInfo` può contenere una stringa `FailureReason`, ma `NSUnderlyingErrorKey` non è sempre popolato: i guasti di associazione al dominio restituiscono nil per l'errore di fondo. |
| 1005     | `NotInteractive`                        | iOS 15    | Nessuna credenziale disponibile quando si utilizza `preferImmediatelyAvailableCredentials`. Questo è il segnale "not found" pulito - l'equivalente iOS di "nessuna passkey esiste su questo dispositivo". Non è stata mostrata alcuna interfaccia utente.                                                                |
| 1006     | `MatchedExcludedCredential`             | iOS 18    | Esiste già una passkey per questo RP su questo dispositivo. Segnale pulito per il rilevamento di duplicati - `userInfo` vuoto, nessun `NSUnderlyingErrorKey`. La classificazione funziona senza il matching delle stringhe.                                                                                            |
| 1007     | `CredentialImport`                      | iOS 18.2  | Importazione credenziali non riuscita.                                                                                                                                                                                                                                                                                 |
| 1008     | `CredentialExport`                      | iOS 18.2  | Esportazione credenziali non riuscita.                                                                                                                                                                                                                                                                                 |
| 1009     | `PreferSignInWithApple`                 | iOS 26    | L'utente preferisce Accedi con Apple alla passkey. Nuovo in iOS 26.                                                                                                                                                                                                                                                    |
| 1010     | `DeviceNotConfiguredForPasskeyCreation` | iOS 26    | Il dispositivo è privo di passcode o configurazione di iCloud Keychain. Bug noto del simulatore iOS 26: restituisce 1010 anche quando la configurazione è corretta (non si riproduce sui dispositivi fisici).                                                                                                          |

La distinzione più importante per la produzione: **da iOS 18, le credenziali duplicate
restituiscono il proprio codice di errore 1006 (`MatchedExcludedCredential`).** Su
iOS 17 e versioni precedenti, le credenziali duplicate erano nascoste
all'interno del codice 1004 (`Failed`). Su
iOS 18+, la distinzione è strutturale
(codice di errore diverso), non testuale.

**Errori di runtime comuni (riferimento a livello di log):**

Questi messaggi compaiono in `localizedDescription` o in `userInfo` per specifici scenari
di errore. Utilizzali per le ricerche nei log e il debugging, non per la classificazione a livello di codice.

| **Messaggio (impostazioni locali inglese)**                                     | **Codice di base**  | **Note**                                                                                                                                         |
| ------------------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Application with identifier <TeamID.BundleID> is not associated with domain X` | 1004 (`Failed`)     | L'entitlement Associated Domains dell'app non corrisponde al relying party. Correggi il file `apple-app-site-association` sul tuo server.        |
| `Couldn't communicate with a helper application.`                               | 1004 (`Failed`)     | L'estensione del provider di credenziali non ha risposto. Temporaneo: è opportuno riprovare.                                                     |
| `Request already in progress for specified application identifier.`             | 1004 (`Failed`)     | Una richiesta ASAuthorization duplicata è stata avviata mentre ne era in sospeso un'altra. Race condition nell'app.                              |
| `Stolen Device Protection is enabled and biometry is required.`                 | 1004 (`Failed`)     | iOS 17+ Stolen Device Protection blocca l'autenticazione biometrica in luoghi non familiari. Non risolvibile dagli sviluppatori, ma vale la pena informare l'utente. |
| `(AuthenticationServicesCore.ASCABLEClient.ClientError error 2.)`               | Dominio separato    | L'handshake Bluetooth dell'autenticazione cross-device (hybrid/CABLE) è fallito.                                                                 |
| `(AuthenticationServicesCore.ASCABLEClient.ClientError error 3.)`               | Dominio separato    | La connessione Bluetooth dell'autenticazione cross-device è fallita.                                                                             |

**Messaggi localizzati "nessuna credenziale" (codice 1005):**

Quando `preferImmediatelyAvailableCredentials` è impostato e non esiste alcuna passkey, iOS restituisce
il codice 1005 (`NotInteractive`) con un messaggio localizzato nella lingua del dispositivo
dell'utente. Ciò è esclusivo delle app native: i browser web non espongono mai questo segnale. Il messaggio inizia sempre
con `The operation couldn't be completed.` seguito dal testo localizzato:

| **Lingua**            | **Messaggio**                                                                        |
| --------------------- | ------------------------------------------------------------------------------------ |
| Cinese (Semplificato) | `没有可用于登录的凭证。`                                                             |
| Vietnamita            | `Không có sẵn thông tin để đăng nhập.`                                               |
| Arabo                 | `لا تتوفر بيانات اعتماد لتسجيل الدخول.`                                              |
| Spagnolo              | `No hay ninguna credencial disponible para iniciar sesión.`                          |
| Cinese (Tradizionale) | `沒有可用於登入的憑證。`                                                             |
| Coreano               | `로그인을 위한 자격 증명이 없습니다.`                                                |
| Francese (Canada)     | `Aucun identifiant disponible pour la connexion.`                                    |
| Portoghese (Brasile)  | `Nenhuma credencial disponível para login.`                                          |
| Francese (Francia)    | `Aucune information d'identification n'est disponible pour procéder à la connexion.` |
| Tailandese            | `ไม่มีข้อมูลประจำตัวสำหรับเข้าสู่ระบบ`                                               |
| Italiano              | `Non ci sono credenziali disponibili per l'accesso.`                                 |
| Olandese              | `Geen inloggegevens beschikbaar.`                                                    |
| Giapponese            | `ログイン用の資格情報がありません。`                                                 |
| Turco                 | `Oturum açmak için kullanılabilecek kimlik bilgisi yok.`                             |

I dispositivi in ​​lingua inglese in genere risolvono l'errore "nessuna credenziale" a livello di API prima che
il framework ASAuthorization restituisca un errore localizzato, motivo per cui non appare alcuna variante
inglese qui sopra. A livello di codice, fai sempre affidamento sul codice 1005 anziché analizzare queste
stringhe.

### 9.2 Android (Credential Manager API)

Android mostra gli errori delle passkey tramite l'API Credential Manager
(`androidx.credentials`). I messaggi di errore includono un messaggio principale e spesso un `cause`
con ulteriori dettagli. Rispetto a iOS, Android fornisce tipi di errore più strutturati e
cause più esplicite per i problemi di configurazione.

**Annullamento dell'utente e rilevamento delle credenziali:**

| **Errore**                                        | **Note**                                                                                                                                                                                                  |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `User cancelled the operation`                    | L'utente ha chiuso il prompt passkey. L'equivalente di `NotAllowedError`. Nota: il Credential Manager restituisce anche `User canceled the request` (ortografia USA) da un percorso di codice diverso - entrambi sono identici. |
| `Excluded credential matches existing credential` | Esiste già una passkey per questo ID credenziale. L'equivalente di `InvalidStateError`. A differenza di iOS, il messaggio è distinto dall'annullamento dell'utente.                                         |
| `No create options available.`                    | Nessun provider di credenziali idoneo può gestire la richiesta di creazione. In genere significa che i Google Play Services sono obsoleti o che nessun provider di credenziali supporta la creazione di passkey. |

**Errori di configurazione e sicurezza:**

| **Errore**                                                                     | **Note**                                                                                                                                          |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Passkeys not supported for this app`                                          | I Digital Asset Links (`assetlinks.json`) mancano o non contengono l'impronta digitale del certificato di firma dell'app. L'equivalente di `SecurityError`. |
| `Https failed: respCode=301, url=https://<domain>/.well-known/assetlinks.json` | Il file `assetlinks.json` restituisce un reindirizzamento invece di un HTTP 200. Android richiede il file all'URL esatto senza reindirizzamenti.                  |
| `The incoming request cannot be validated`                                     | Il Credential Manager non può verificare la richiesta tramite Digital Asset Links.                                                                  |
| `RP ID cannot be validated.`                                                   | Il relying party ID nelle opzioni WebAuthn non corrisponde a `assetlinks.json`.                                                                   |
| `Screen lock is missing.`                                                      | Nessun PIN, sequenza o dato biometrico configurato sul dispositivo. Le passkey richiedono la verifica dell'utente. L'equivalente di `ConstraintError`.            |
| `Cannot find an eligible account.`                                             | Nessun account Google sul dispositivo è idoneo per la creazione della passkey (raro, in genere configurazioni aziendali personalizzate).          |

**Errori della piattaforma e dell'autenticatore:**

| **Errore**                                                                               | **Note**                                                                                                                                                                                                                                           |
| ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Unsuccessful result from folsom activity.`                                              | Fallimento interno ai Google Play Services. "Folsom" è un componente GMS per le operazioni passkey. Temporaneo: è opportuno riprovare.                                                                                                             |
| `Can't find the proper key to decrypt the private key from WebauthnCredentialSpecifics.` | Esiste una passkey sincronizzata ma il dispositivo non può decrittografare la sua chiave privata. Lo stato di sincronizzazione del Google Password Manager è incoerente: la credenziale è stata sincronizzata da un altro dispositivo ma la chiave di decrittografia non è disponibile. Non risolvibile dagli sviluppatori. |
| `Operation was interrupted` (cause: `The UI was interrupted - please try again.`)        | L'interfaccia utente del Credential Manager è stata interrotta da un'altra attività (chiamata in arrivo, rotazione dello schermo, app in background). L'equivalente di `AbortError`.                                                               |
| `Unknown credential error`                                                               | Un errore catch-all generico quando non si applica alcun tipo di errore specifico. Solitamente transitorio.                                                                                                                                        |
| `timeout` (cause: `Canceled`)                                                            | L'operazione del Credential Manager è andata in timeout prima che l'utente completasse la verifica biometrica.                                                                                                                                     |

## 10. Mettere tutto insieme: la tassonomia degli errori

Il diagramma seguente mostra come tutti i livelli descritti in precedenza (infrastruttura,
ambiente, tipo di operazione, classificazione e rilevamento) si connettono da un'estremità all'altra. È il
modello mentale da tenere a mente quando si progetta il tracciamento degli errori.

```mermaid
flowchart TD
    %% Global Styles
    classDef expected fill:#e3f2fd,stroke:#1565c0,stroke-width:2px;
    classDef unexpected fill:#ffebee,stroke:#c62828,stroke-width:2px;
    classDef network fill:#fff3e0,stroke:#ef6c00,stroke-dasharray: 5 5;
    classDef env fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1px;
    classDef action fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;

    %% --- LEFT SIDE: NETWORK & SERVER ---
    subgraph Infrastructure ["Infrastruttura (Server/Rete)"]
        direction TB
        ServerErr["Errore lato server<br/>500 / Errore logico"]:::network
        NetErr["Errore di rete<br/>Timeout / 400 Bad Request"]:::network

        ServerErr & NetErr -->|Si manifesta come| ClientManifest["Errore generico lato client"]
    end

    %% --- CENTER: THE ENVIRONMENT STACK (From Sketch) ---
    subgraph Environment ["L'ambiente (Lato client)"]
        direction TB

        %% Layer 1: Platform Type
        subgraph Type ["Livello 1: Piattaforma"]
            Web["Web / Browser"]
            Native["Nativo / App"]
        end

        %% Layer 2: OS Context
        subgraph OS_Layer ["Livello 2: OS e versione"]
            OS_Web["OS: Windows, macOS, Linux"]
            OS_Nat["OS: iOS, Android"]
            OS_Ver["Versione OS / Impostazioni<br/>(Blocco schermo, Biometria)"]

            Web --> OS_Web
            Native --> OS_Nat
            OS_Web & OS_Nat --> OS_Ver
        end

        %% Layer 3: Client Software
        subgraph Client_Layer ["Livello 3: Software client"]
            Browser["Browser: Chrome, Safari, Edge<br/>+ Versione"]
            App["App nativa / WebView<br/>+ Versione app"]

            OS_Ver --> Browser
            OS_Ver --> App
        end

        %% Layer 4: WebAuthn Operation
        subgraph Op_Layer ["Livello 4: Operazione WebAuthn"]
            OpType{"Tipo di operazione"}

            Login["Login<br/>(navigator.credentials.get)"]
            Reg["Registrazione<br/>(navigator.credentials.create)"]

            Browser & App --> OpType
            OpType --> Login
            OpType --> Reg
        end

        %% Layer 5: Sub-types & Complexity
        subgraph Modality ["Livello 5: Modalità e complessità"]
            Sub_CUI["CUI / Conditional UI"]
            Sub_Auto["Operazione automatica"]
            Sub_CDA["CDA / Cross-Device Auth"]

            Login & Reg --> Sub_CUI
            Login & Reg --> Sub_Auto
            Login & Reg --> Sub_CDA
        end
    end

    %% --- BOTTOM: CLASSIFICATION & ACTION (The "What here?" section) ---
    subgraph Analysis ["Analisi degli errori e runtime"]
        direction TB

        %% Classification
        Classification{"Classificazione"}

        Exp["Errore previsto<br/>(L'utente ha interrotto la cerimonia)"]:::expected
        Unexp["Errore imprevisto<br/>(Sistema/Crash/Sconosciuto)"]:::unexpected

        ClientManifest --> Classification
        Sub_CUI & Sub_Auto & Sub_CDA --> Classification

        Classification --> Exp
        Classification --> Unexp

        %% Detection Logic
        subgraph Detection ["Obiettivi di rilevamento"]
            P1["P1: Problemi di login"]
            P2["P2: Problemi di creazione"]
            Baseline["Rileva deriva della baseline<br/>(Aumento previsti)"]
            NewErr["Rileva nuove anomalie<br/>(Aumento imprevisti)"]
        end

        Exp --> Baseline
        Unexp --> NewErr
        Exp & Unexp --> P1 & P2

        %% Actionable Outcomes
        subgraph Action ["Azioni di mitigazione"]
            Fix["Rilascia Hotfix / Nuova versione"]:::action
            Fallback["Attiva Fallback automatico<br/>(Disabilita Passkey)"]:::action
        end

        P1 & P2 & NewErr --> Fix
        P1 & P2 & NewErr --> Fallback
    end

    %% Connectors
    Infrastructure -.-> Environment
```

L'intuizione chiave: un `error.name` non elaborato ha senso solo quando sai quale livello dell'
ambiente lo ha prodotto, quale operazione era in esecuzione e se l'errore era previsto o
imprevisto. Le sezioni sottostanti descrivono cosa registrare e come agire di conseguenza.

## 11. Cosa registrare in modo che gli errori diventino debuggabili

La maggior parte della classificazione degli errori in questo articolo può essere eseguita solo con i segnali
lato client. Un SDK di osservabilità solo frontend acquisisce abbastanza contesto per classificare
la stragrande maggioranza degli errori WebAuthn. Questo è anche il modo in cui
è architettato l'SDK di osservabilità di Corbado: il livello lato client gestisce l'attribuzione degli errori,
le tempistiche, il contesto dell'operazione e il rilevamento della piattaforma. La registrazione lato server aggiunge un
secondo livello per i guasti che solo il backend può vedere.

Il requisito chiave: ogni tentativo deve essere rintracciabile da un capo all'altro. Un id di correlazione condiviso
(ad esempio `auth_flow_id`) collega il contesto lato client con l'esito della verifica del server.

### 11.1 Segnali lato client (SDK frontend)

| **Segnale**                                | **Perché è importante**                                                                                                                              |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `error.name` + motivo normalizzato         | Errore raw del browser + la tua classificazione                                                                                                      |
| Tipo di operazione                         | Conditional UI, login modale, creazione manuale, conditional create, auto-append. **CDA (Cross-Device Auth)** è una complessità a sé stante.           |
| Contesto ambientale completo               | OS + versione, browser + versione, **marca/modello hardware**, **impostazioni dell'autenticatore** (es. GPM abilitato?)                              |
| Contesto dell'autenticatore/del manager    | Rotture delle estensioni e del provider                                                                                                              |
| Tempo trascorso prima dell'errore          | Rifiuto immediato (`<1s`) vs annullamento dell'utente (1-15s) vs timeout (30s+)                                                                      |
| Stato della rete/connettività              | Gli errori di rete spesso si manifestano come errori client. Tieni traccia se l'utente era offline e **metti in coda i log** da inviare al ripristino. |
| Se è apparsa la UI QR/ibrida               | Fallimento locale vs cross-device                                                                                                                    |
| Id di correlazione (`auth_flow_id`)        | Collega con i log del server                                                                                                                         |

### 11.2 Segnali lato server (Verifica del backend)

I fallimenti della verifica lato server si verificano dopo che il browser ha restituito una credenziale e
una challenge firmata. Questi dovrebbero essere errori strutturati con codici espliciti, e non mescolati nello
stesso bucket dei nomi delle `DOMException` lato client.

| **Segnale**                                 | **Perché è importante**                                                                                                                                                                                                              |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Mancata corrispondenza / challenge scaduta  | Problemi di tempistiche della sessione o di replay                                                                                                                                                                                   |
| Origine / mancata corrispondenza dell'RP ID | Bug di configurazione multi-dominio                                                                                                                                                                                                  |
| Firma non valida / credenziale non trovata  | Credenziale eliminata o corrotta. Caso comune: accesso con Conditional UI con una passkey che l'utente ha già eliminato lato server. Usa la Signal API per mantenere sincronizzati gli elenchi di credenziali client e server. |
| Mancata corrispondenza dell'handle utente   | Problemi di mappatura dell'account                                                                                                                                                                                                   |
| Id di correlazione (`auth_flow_id`)         | Collega con il contesto lato client                                                                                                                                                                                                  |

Se desideri un modello a imbuto completo (drop-off in base allo step e conversione tra gli step), vedi l'
analisi della telemetria delle passkey.

[Watch on YouTube](https://www.youtube.com/watch?v=wnrXJzvBjsU)

Con questi dati in atto, la conclusione diventa semplice: la maggior parte degli "errori" si trasforma in correzioni della UX,
correzioni della copertura o correzioni di configurazione. Ma costruire e mantenere questa
classificazione da soli è un notevole lavoro continuo.

## 12. Oltre i nomi degli errori: come Corbado trasforma gli errori raw in segnali azionabili

La checklist di logging mostrata sopra acquisisce segnali raw. Nella produzione su larga scala,
`error.name` da solo non è sufficiente. Costruire questa classificazione da solo è un lavoro notevole e
continuo: i messaggi di errore cambiano a ogni release di browser e sistema operativo,
i fornitori di password manager distribuiscono aggiornamenti che alterano
il comportamento delle cerimonie e nuove firme di errori appaiono con il lancio di ogni funzionalità.

### 12.1 Perché `error.name` da solo non è sufficiente

Lo stesso `NotAllowedError` può significare sei cose diverse a seconda di tre dimensioni
che i browser non separano per te:

| **Dimensione**               | **Cosa ti danno i browser** | **Cosa ti serve effettivamente**                                                       | **Esempio**                                                                                                    |
| ---------------------------- | --------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| **Contesto dell'operazione** | `NotAllowedError`           | È stato un conditional UI, login modale, creazione manuale, conditional create o auto-append? | Android restituisce lo stesso "unknown error" per un login annullato (previsto) e una creazione fallita (imprevisto) |
| **Tempistiche**              | Nessun dato sulla durata    | Rifiuto immediato (`<1s`) vs annullamento utente (1-15s) vs timeout (30s+)             | 200ms = rifiuto ambiente; 5s = l'utente ha visto la finestra e ha annullato; 35s = timeout                     |
| **Piattaforma + auth**       | `error.name` generico       | OS, browser, versione, gestore credenziali per ogni errore                             | "l'utente ha chiuso la finestra" su Chrome e "autofill non disponibile" su Safari emergono entrambi come `NotAllowedError` |

### 12.2 Cosa acquisisce l'SDK di osservabilità di Corbado

Questo è il problema che l'[SDK di osservabilità di Corbado](https://www.corbado.com/pricing) è
concepito per risolvere. È una leggera integrazione frontend che si sovrappone alla tua implementazione passkey
esistente, funziona con qualsiasi server WebAuthn e qualsiasi IDP e classifica automaticamente ogni
errore WebAuthn lungo tutte e tre le dimensioni:

| **Capacità**                            | **Cosa fa**                                                                                                                                                                                                                                                           |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Attribuzione degli errori**           | Acquisisce sistema operativo, versione OS, browser, versione browser e autenticatore a ogni tentativo di cerimonia                                                                                                                                                    |
| **Modalità operativa**                  | Collega ogni errore all'operazione specifica (conditional UI, login modale, creazione manuale, conditional create, auto-append) in modo che lo stesso `NotAllowedError` si risolva in cause radice differenti                                                         |
| **Tempistiche dall'inizio dell'azione** | Registra la durata dall'avvio della cerimonia per distinguere i rifiuti immediati, gli annullamenti degli utenti e i timeout senza dover indovinare                                                                                                                   |
| **Classificazione intelligente**        | Mappa il contesto completo dell'errore (non solo il nome), normalizzando tra diversi **Ambienti** (OS, hardware, impostazioni). Dà priorità a gruppi di errore distinti come **CDA** vs Locale e distingue gli errori **Previsti (Annullamento Utente)** da quelli **Imprevisti (Guasto Sistema)**. |
| **Frammentazione dei gestori di pwd**   | Rileva quando le estensioni di gestione credenziali (Bitwarden, 1Password, LastPass) intercettano le cerimonie e restituiscono risposte non standard, separando i guasti causati dall'estensione da quelli della piattaforma                                          |

Questo è il livello **Observe**: visibilità su ciò che sta accadendo, senza cambiare la tua
implementazione.

### 12.3 Due modi di debuggare: Top-Down e Bottom-Up

La console di gestione di Corbado supporta due percorsi di indagine:

**Top-down (dalla dashboard alla causa radice):**

1. **Monitoraggio di due tipi distinti di anomalie:**
    - **Aumento degli errori previsti (Deriva della baseline):** Un ambiente specifico (es.
      iOS 18.2 su iPhone 15) ha registrato un
      graduale aumento degli "annullamenti utente"? Spesso questo indica un attrito nell'UX introdotto da
      un aggiornamento del sistema operativo.
    - **Aumento degli errori imprevisti (Picchi):** Un errore completamente nuovo o un aumento improvviso nei
      guasti. Questo indica solitamente un cambiamento che provoca interruzioni (aggiornamento interno dello stack IDP) o una
      regressione in una nuova versione del browser.
2. **Priorità in base all'impatto:**
    - **P1: Problemi di login.** Se i tassi di successo del login scendono, genera un avviso immediato.
    - **P2: Problemi di creazione.** Monitora le tendenze, ma evita di svegliare gli ingegneri per picchi di "annullamenti
      utente" nei flussi di creazione.
3. **Analisi approfondita dell'Ambiente:** Usa le dimensioni granulari (Hardware, Impostazioni Auth)
   per isolare se il problema è globale o specifico per i "dispositivi Samsung
   con Android 14".
4. **Mitigazione:** Se viene rilevato un picco critico, tieni pronto un **Kill Switch**.
   Disabilita automaticamente o manualmente le passkey per quello specifico ambiente ed esegui il fallback
   a OTP/Password per preservare i tassi di login mentre indaghi.

**Bottom-up (dai modelli di errore all'impatto):**

1. Inizia dalla visualizzazione della classificazione degli errori. Esamina i pattern degli errori classificati e i loro
   volumi.
2. Perfeziona le mappature degli errori man mano che emergono nuovi pattern (ad esempio, una nuova versione del browser rilascia un
   messaggio di errore diverso).
3. Gli errori vengono correlati in modo incrociato con le variazioni dei KPI e visualizzati come annotazioni sulle dashboard,
   quindi un picco in un pattern di errore specifico è automaticamente collegato alla metrica che
   ha influenzato.

Entrambi i percorsi convergono: il top-down ti dice che qualcosa non va, il bottom-up ti dice perché. L'
[AI Analytics Assistant](https://www.corbado.com/ai-analytics-assistant) connette i due consentendoti di porre
domande in linguaggio naturale sia sui dati degli errori che sulle metriche di adozione.

I team che desiderano agire su questi segnali possono passare ad **Adopt**, che aggiunge la
[passkey intelligence](https://docs.corbado.com/corbado-connect/features/passkey-intelligence)
per limitare automaticamente le cerimonie, ottimizzare i prompt di registrazione e ripristinare gli stati interrotti
delle passkey. Per ambienti regolamentati o implementazioni su larga scala, **Enterprise** aggiunge l'
hosting single-tenant, l'integrazione SIEM e la configurazione
conforme alla PSD2.

## 13. Conclusione

I nomi degli errori WebAuthn non sono un verdetto. Sono dei suggerimenti e diventano utilizzabili
solo quando li colleghi al tipo di operazione, alle tempistiche e al contesto della piattaforma.

- **Cosa significano in produzione i nomi di errore WebAuthn più comuni?** La maggior parte si mappa a un set ridotto di livelli: flusso di controllo utente (`NotAllowedError`), ciclo di vita dell'app/concorrenza (`AbortError`), contesto/configurazione di sicurezza (`SecurityError`), o bug di opzioni/stato (`InvalidStateError`, `ConstraintError`, `DataError`). La stragrande maggioranza del volume è formata da `NotAllowedError`, e la maggior parte di questi è un comportamento previsto (l'utente ha chiuso il prompt).
- **Come disambiguare `NotAllowedError`?** Usa le tempistiche (rifiuto immediato vs annullamento utente vs timeout), un indicatore QR/ibrido (mancata corrispondenza della disponibilità), il contesto di attivazione dell'utente (soprattutto su iOS/Safari) e il tipo di operazione (conditional UI vs login modale vs creazione passkey vs conditional create). Non trattare tutti i `NotAllowedError` come un'unica modalità di fallimento.
- **Perché il tipo di operazione è importante?** Lo stesso `error.name` durante un
  login con conditional UI è un segnale completamente diverso rispetto a
  durante un conditional create o una creazione manuale di passkey (l'utente ha ignorato la finestra).
  La registrazione del tipo di operazione insieme all'errore è ciò che trasforma il generico `NotAllowedError`
  in un bucket azionabile.
- **Quale contesto minimo rende debuggabili gli errori?** Registra `error.name`, il tipo di operazione,
  il tempo trascorso prima dell'errore dall'inizio dell'operazione, il tipo di flusso, se è stata mostrata l'interfaccia utente QR/ibrida,
  il sistema operativo/browser/dispositivo (comprese le versioni), un id di correlazione (`auth_flow_id`) e i rifiuti di verifica del server
  come codici espliciti.

Due regole pratiche che si applicano a tutti i tipi di errore: non mostrare mai agli utenti gli errori raw del browser (fornisci sempre un chiaro percorso di fallback) e separa i tentativi locali da quelli QR/ibridi tra più dispositivi, perché falliscono per motivi diversi e necessitano di correzioni diverse.
Su larga scala, mantenere la classificazione degli errori attraverso browser, versioni del sistema operativo e gestori di credenziali è un lavoro continuo. Valuta l'utilizzo di un SDK di osservabilità con una libreria di pattern gestita anziché costruire tutto da zero.

## Domande frequenti

### Come faccio a distinguere tra un utente che annulla un prompt per le passkey e il dispositivo che non possiede una passkey?

Sul web, i browser raggruppano entrambi i casi in `NotAllowedError` per motivi di privacy, quindi
non puoi distinguerli direttamente. Usa le tempistiche come proxy: un errore sotto a 1 secondo in genere
significa un rifiuto da parte dell'ambiente o l'assenza di capacità, mentre un lasso di tempo di 1-15 secondi suggerisce che l'utente ha
visto e chiuso la finestra di dialogo. Sulle app native per iOS e Android, l'impostazione di
`preferImmediatelyAvailableCredentials` prima della richiesta fornisce un segnale pulito di "nessuna credenziale" (codice iOS 1005, Android `GetCredentialRequest`) prima ancora che venga mostrata un'interfaccia utente.

### Perché il mio tasso di errori WebAuthn è così alto anche se le passkey sembrano funzionare per la maggior parte degli utenti?

Nelle implementazioni ottimizzate di passkey su larga scala, oltre il 95% degli errori WebAuthn registrati
sono annullamenti previsti dall'utente, non guasti di sistema. Il tracciamento dei conteggi raw di `error.name` senza
separare "l'utente ha chiuso il prompt" dai guasti veri e propri gonfia le metriche degli errori e nasconde
delle reali regressioni tra i volumi dei `NotAllowedError`. Dividi i tuoi conteggi in base al tipo di
operazione e tratta i bucket di annullamenti utente separatamente da errori inattesi come
`SecurityError`, `ConstraintError` e `DataError`.

### Cosa significa il codice 1005 di ASAuthorizationError su iOS e come dovrei usarlo?

Il codice iOS 1005 (`NotInteractive`) significa che nessuna passkey era disponibile sul dispositivo quando
`preferImmediatelyAvailableCredentials` è stato impostato su `ASAuthorizationController` e non è stata
mostrata alcuna UI all'utente. Questo è il segnale pulito "nessuna passkey esiste su questo dispositivo" che
i browser web non possono fornire a causa dei vincoli di privacy. Classifica sempre sul
codice numerico, non su `localizedDescription`, perché i messaggi Apple sono localizzati in più di 30
lingue e possono cambiare tra le varie versioni del sistema operativo.

### Come dovrei gestire il NotAllowedError durante un flusso di conditional create o di aggiunta della passkey ad attivazione automatica?

Durante il conditional create, `NotAllowedError`, `AbortError` e `InvalidStateError` sono tutti
risultati previsti e dovrebbero essere ignorati silenziosamente anziché fatti emergere come errori.
`NotAllowedError` indica che l'autofill non è disponibile o che la pagina ha perso il focus, mentre
`InvalidStateError` significa che una passkey esiste già nel provider. Prima di tentare la
chiamata, usa `getClientCapabilities()` per verificare il supporto al `conditionalCreate` e controlla lo stato
di visibilità del documento per evitare di gonfiare i tuoi conteggi di errore.

### Quale contesto dovrei registrare accanto a error.name per rendere effettivamente debuggabili i fallimenti di WebAuthn?

Acquisisci il tipo di operazione (conditional UI, login modale, creazione manuale, conditional create o auto-append), il tempo trascorso prima dell'errore, la versione del sistema operativo, la versione del browser, il modello
hardware, se è apparso l'elemento QR/ibrido nell'interfaccia e un ID di correlazione da collegare ai log del lato server. I fallimenti
nella verifica del server come la challenge errata, la firma non valida e la credenziale non trovata
dovrebbero essere registrati come codici espliciti strutturati, non raggruppati nello stesso bucket
insieme ai nomi `DOMException` del lato client. Questi segnali, sommati fra loro, consentono la classificazione della stragrande
maggioranza degli errori in bucket azionabili, senza dover tirare a indovinare.
