Errori e fix

Registry 401, 402, 403: token, piani e permessi senza indovinare

Tre codici, tre cause diverse: il 401 è identità, il 402 è pagamento, il 403 è diritto di accesso. La diagnosi parte dal comando, non dal browser.

In sintesi

  • 401: header assente, malformato o token scaduto. 402: il registry richiede un pagamento. 403: token valido ma senza diritto su quella risorsa.
  • Il token va passato come header configurato in `components.json`, letto da una variabile d'ambiente.
  • Un token committato è un token compromesso: ruotalo prima di ogni altra correzione.

Risposta breve: 401 significa identità mancante o non valida, 402 significa pagamento richiesto, 403 significa identità valida ma senza diritto sulla risorsa. Diagnostica con curl prima di toccare la configurazione.

La tabella dei codici

CodiceSignificato praticoPrima azione
401header assente, token scaduto o malformatoverifica il valore e la scadenza
402il piano non include la risorsacontrolla l'abbonamento, non il token
403token valido, permesso assentecontrolla lo scope o il namespace
404namespace o nome erratoverifica lo slug nel registry

Il meccanismo: l'ordine in cui il registry risponde

Un registry privato controlla tre cose in sequenza: chi sei, se hai pagato, cosa puoi leggere. Il primo controllo produce 401 quando l'header manca, è malformato o è scaduto. Il secondo produce 402 quando l'identità è valida e l'entitlement no. Il terzo produce 403 quando l'identità è valida, il piano esiste e lo scope o il namespace non coprono quella risorsa. L'ordine conta: un token scaduto su una risorsa a pagamento restituisce 401, non 402, quindi il codice da solo non racconta tutto. Il CLI stampa lo status e scarta il corpo, che è dove il registry scrive la ragione.

Un passo diagnostico: leggi il corpo, non solo il codice

Il comando qui sotto stampa in una riga lo status, l'header WWW-Authenticate e i primi caratteri del corpo: il primo parla del gateway, l'ultimo del registry. Se la variabile è vuota, l'header parte senza token e il 401 è già spiegato. Se WWW-Authenticate manca, la risposta non viene dal servizio ma da un proxy davanti. Se il corpo nomina uno scope, il token è buono e il permesso no.

node -e "fetch('https://registry.acme.dev/r/button.json',{
  headers:{Authorization:'Bearer '+process.env.ACME_REGISTRY_TOKEN}
}).then(async r=>console.log(
  r.status, r.headers.get('www-authenticate'), (await r.text()).slice(0,120)))"

Configurazione corretta

// components.json
{
  "registries": {
    "@acme": {
      "url": "https://registry.acme.dev/r/{name}.json",
      "headers": {
        "Authorization": "Bearer ${ACME_REGISTRY_TOKEN}"
      }
    }
  }
}

Il token sta in una variabile d'ambiente, non nel file. Se il file è già stato committato con il token in chiaro, ruotalo: rimuoverlo dalla storia non annulla la copia che qualcuno ha già scaricato.

Verifica senza installare

curl -i -H "Authorization: Bearer $ACME_REGISTRY_TOKEN" \
  https://registry.acme.dev/r/button.json

Il codice di stato risponde alle tre domande in un colpo: se è 200 il problema è nella configurazione del client; se è 401 il token non arriva; se è 403 il token è riconosciuto e manca il permesso.

Quando la correzione non funziona

Se curl funziona e il client no, la variabile non arriva al processo. Succede in CI senza secret, in container senza env_file, o in un comando eseguito da un contesto che non eredita l'ambiente. Verifica con node -p process.env.ACME_REGISTRY_TOKEN nello stesso contesto. Un secondo caso è un proxy che rimuove l'header Authorization: in quel caso il 401 arriva dal gateway, non dal registry.

Quando la correzione comune peggiora le cose

Davanti a un 401 la reazione è rigenerare il token. Se il 401 veniva da un proxy che rimuove Authorization, il token nuovo non cambia nulla e ora hai due variabili: un secret ruotato e un header ancora tagliato. Ruotare senza aver isolato il livello rende il problema più difficile da leggere, perché il vecchio token funzionava in curl e il nuovo no. Isola prima: stesso comando, stessa variabile, dal contesto in cui gira il client. Se curl passa e il client no, il token è sano e il problema è il trasporto.

Un limite con un nome: due 401 identici

Il codice 401 non distingue «token mai arrivato» da «token arrivato e rifiutato»: entrambi sono 401, e un gateway può generarne uno suo prima che la richiesta tocchi il registry. Il limite si chiama indistinguibilità del 401; si riduce con header aggiuntivi, un WWW-Authenticate che porta il motivo, un X-Registry-Request-Id da correlare nei log, oppure dando al gateway e al servizio due codici diversi: scelta che molti registry non fanno.

Come prevenirlo

  • Un token per registry, con scadenza breve e permessi minimi.
  • Nessun token in components.json, nemmeno come placeholder reale.
  • Un test in CI che installa un componente da ogni registry configurato.

Il test in CI vale più di una checklist perché vede il registry reale. Aggiungi un secondo controllo sul 402: un endpoint che risponde 402 senza un alert è un cliente che scopre il problema al momento dell'installazione, non prima.

Dove continuare

Nel catalogo: componenti, blocco pricing, icone e classifica dei cataloghi. Le guide: registry privati, token e namespace e temi gratuiti e premium; il confronto: 1.556 blocchi con preview.

Domande frequenti

401 e 403 non sono la stessa cosa?

No. Il 401 dice che non sai chi sei; il 403 dice che sai chi sei ma non puoi accedere a quella risorsa. Le correzioni sono diverse.

Dove metto il token?

In una variabile d'ambiente letta da `components.json`, mai in chiaro nel file committato. In CI la variabile arriva dal secret store.

Come verifico il token senza installare nulla?

Con `curl -i -H "Authorization: Bearer $TOKEN" `. Il codice di stato ti dice se il problema è il token o la risorsa.

Il 402 può arrivare anche con un token valido?

Sì: significa che l'identità è riconosciuta ma il piano non include quella risorsa. Serve un acquisto o un upgrade, non un altro token.

Posso usare registry pubblici senza header?

Sì, se il registry non richiede autenticazione. La configurazione dei namespace resta utile per l'ordine di risoluzione.