Tema
Runbook: OAuth2 per una casella Google (IMAP + SMTP)
Come si ottiene il refresh token di una casella Gmail o Workspace, e come si configura l'ETL per usarlo.
Serve quando IMAP risponde:
IMAP connessione su imap.gmail.com: autenticazione rifiutata — NO [ALERT] ...Google ha tolto l'accesso IMAP con la password dell'account: una password normale viene rifiutata anche se è giusta. Restano due vie — una password per le app, oppure OAuth2. Questo runbook copre la seconda, l'unica adatta a un servizio che deve girare da solo.
Chi lo esegue. Una persona sola, che abbia sia l'accesso alla Google Cloud Console del dominio, sia la possibilità di accedere alla casella. Servono entrambe le cose nello stesso momento, e la seconda quasi sempre è del cliente. Conta 15–20 minuti.
1. Il progetto e l'app
Una volta per casella, sulla Google Cloud Console.
Accedi con un account del dominio e, dal selettore in alto, usa un progetto esistente o creane uno nuovo (per esempio
noeva-etl-posta).Vai su Google Auth Platform (
console.cloud.google.com/auth/overview). Se l'app non è ancora registrata la console lo chiede adesso: è obbligatorio prima di poter creare un client. Compila nome dell'app, email di supporto e contatto.Tipo di utente:
Internal. È la scelta che conta, e va fatta ora perché cambiarla dopo invalida il lavoro. Vincola l'app al vostro dominio, non richiede alcuna verifica da parte di Google per lo scope della posta — che è classificato restricted — ed evita la scadenza a sette giorni del refresh token, che colpisce solo le appExternalin statoTesting.Lo verifichi nella pagina Audience (
console.cloud.google.com/auth/audience): il campoUser typedeve direInternal.Externalserve solo per una casella Gmail personale, e in quel caso devi anche portarePublishing statusaIn production, o il token ti muore dopo una settimana.Data access → aggiungi lo scope
https://mail.google.com/. Uno solo: copre IMAP e SMTP insieme.
2. Il client → CLIENT_ID e CLIENT_SECRET
Google Auth Platform → Clients (
console.cloud.google.com/auth/clients) → CREATE CLIENT.Application type:
Web application.⚠️ Non «Desktop app», anche se il worker ETL è a tutti gli effetti un servizio. Serve un tipo di client su cui si possa dichiarare un redirect URI, e per i client Desktop la console — testualmente — «non richiede alcuna informazione aggiuntiva»: quel campo non esiste, e il passo 8 diventa impossibile.
Dai un nome al client (per esempio
noeva-etl).In Authorized redirect URIs → ADD URI, incolla esattamente, senza barra finale:
https://developers.google.com/oauthplaygroundCREATE.
⚠️ Copia subito il Client secret. Da aprile 2025 Google lo mostra una volta sola, alla creazione: dopo ne vedrai solo le ultime quattro cifre. Se lo perdi non si recupera — si ruota, e ruotarlo invalida la configurazione già in uso. Copia anche il Client ID, che invece resta sempre visibile.
→ Hai OAUTH_CLIENT_ID e OAUTH_CLIENT_SECRET.
3. Il consenso → REFRESH_TOKEN
Il refresh token si ottiene con un consenso interattivo, una volta sola. Serve un browser e la casella.
Apri OAuth 2.0 Playground.
Clicca l'ingranaggio in alto a destra e verifica questi tre campi. Sono già i valori di default, ma controllali: se
Access typenon èOfflinenon riceverai alcun refresh token, che è l'unica cosa che ci serve.Campo Valore OAuth flow Server-sideAccess type OfflineForce prompt Consent ScreenSempre nell'ingranaggio, spunta Use your own OAuth credentials e incolla Client ID e Client secret.
Chiudi il pannello. In fondo all'elenco delle API, nella colonna di sinistra, c'è il campo «Input your own scopes»: scrivici
https://mail.google.com/, con la barra finale.Authorize APIs. Si apre l'accesso Google: entra con la casella che l'ETL dovrà leggere, non con il tuo account né con un amministratore. Il consenso vale per l'account che lo dà.
Accetta il consenso.
Nello Step 2, clicca Exchange authorization code for tokens.
Copia il valore del campo Refresh token (comincia con
1//).
→ Hai OAUTH_REFRESH_TOKEN.
Il refresh token è una credenziale con la stessa forza della password della casella: non passa da chat, ticket o mail. Va direttamente nel
.envdella macchina che esegue il worker.
4. Pulizia
- Nel Playground riapri l'ingranaggio e svuota i campi Client ID e secret: li conserva nelle impostazioni del browser.
- Facoltativo ma consigliato: torna sul client nella console e rimuovi il redirect URI del Playground. Il refresh token già ottenuto continua a funzionare — lo scambio con l'access token non usa alcun redirect — quindi toglierlo non rompe niente. Se un giorno dovrai rifare il consenso, lo riaggiungi.
5. La configurazione
Nel .env del worker, tre variabili per l'identità e due righe per dire ai connettori di usarla:
bash
# L'identità OAuth2 (una per casella: legge e scrive con lo stesso consenso)
OAUTH_CLIENT_ID=<client id della console>
OAUTH_CLIENT_SECRET=<client secret della console>
OAUTH_REFRESH_TOKEN=<il refresh token del passo 18>
# IMAP passa a OAuth2 (e la password va TOLTA, non commentata a metà)
IMAP_AUTH=oauth2
IMAP_HOST=imap.gmail.com
IMAP_PORT=993
IMAP_SECURE=true
IMAP_USER=<la casella>
# SMTP, stessa identità
SMTP_AUTH=oauth2
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=<la casella>
SMTP_FROM=<mittente, per esempio: Noeva ETL <la-casella>>IMAP_PASSWORD e SMTP_PASSWORD vanno rimosse: AUTH=oauth2 con una password ancora impostata è un errore di configurazione, e lo è di proposito — il metodo di autenticazione dev'essere una scelta esplicita, non il risultato di quale variabile è rimasta. Non serve cancellare le righe: una variabile valorizzata a stringa vuota conta come assente.
⚠️ SMTP_USER è obbligatoria e nessuno te lo dirà. Con SMTP_AUTH=oauth2 ma senza SMTP_USER, la configurazione non fallisce: il connettore conclude che il server è un relay interno senza autenticazione e prova a inviare in chiaro. Te ne accorgi da Gmail che rifiuta l'invio con un messaggio che non parla di OAuth. Su IMAP il problema non c'è, IMAP_USER è sempre obbligatoria.
IMAP_OAUTH_INSTANCE e SMTP_OAUTH_INSTANCE non servono finché l'identità è una sola: entrambi valgono default. Servono con più caselle, insieme a OAUTH_INSTANCES.
6. La verifica
bash
pnpm etl run test-postaÈ la pipeline che esiste per questo: esercita IMAP e SMTP e nient'altro. Vedi la sua scheda. Se la mail di riepilogo arriva, l'identità funziona per entrambi i protocolli e puoi accendere la pipeline vera.
Se extracted è 0 la casella era vuota: IMAP si è collegato, ma di SMTP non sai ancora nulla. Serve almeno un messaggio nella cartella perché la prova sia completa.
Che cosa fa morire un refresh token
Su un progetto Internal il token non scade da solo. Smette di funzionare per una di queste ragioni, e vale la pena conoscerle prima di incontrarle:
| Causa | Quanto è probabile |
|---|---|
| La password della casella è cambiata | Alta, ed è quella che sorprende sempre. Lo scope https://mail.google.com/ è un ambito Gmail, e Google invalida i token con ambiti Gmail a ogni cambio password. Nessuno collega «ho cambiato la password della posta» a «l'ETL si è fermato» |
| Il consenso è stato revocato | Media. Succede dalle impostazioni di sicurezza dell'account, anche per sbaglio, durante una pulizia delle app autorizzate |
| Un amministratore ha limitato il servizio nel dominio | Bassa, ma l'errore lo dice (admin_policy_enforced) |
| Il token non è stato usato per sei mesi | Nulla su una pipeline schedulata; reale su un'identità di prova lasciata lì |
| Superato il limite di 100 refresh token per account e client | Bassa. Rifare il consenso molte volte sullo stesso client invalida i più vecchi, silenziosamente |
In tutti i casi il rimedio è lo stesso — rifare i passi 11–18 — ma sapere quale dei cinque è stato cambia chi devi chiamare.
⚠️ Pratica utile: quando il cliente ha in programma un cambio password della casella, mettete in conto di rifare il consenso subito dopo. È l'unico modo di non scoprirlo dal monitoraggio.
Anche il client OAuth si autodistrugge se inutilizzato. Google cancella i client fermi da sei mesi, con un preavviso email trenta giorni prima all'indirizzo di contatto del progetto. Per una pipeline schedulata è irrilevante, ma è un motivo per mettere al passo 2 un indirizzo che qualcuno legge davvero: è lo stesso canale da cui arriveranno gli avvisi che contano.
Guasti, e che cosa vogliono dire
| Messaggio | Che cosa è successo |
|---|---|
OAuth2: refresh token rifiutato (invalid_grant) | Il token non è più valido: vedi la tabella delle cause qui sopra. Su Internal, per prima cosa chiedi se è cambiata la password della casella |
OAuth2: il provider ha rifiutato la richiesta (invalid_client…) | Client ID o secret sbagliati, o di un altro progetto |
OAuth2: il token endpoint non risponde | Rete, DNS, timeout, o un redirect rifiutato. Non è la configurazione: non rileggere il .env |
OAuth2: risposta del provider senza access_token utilizzabile | Il provider ha risposto 200 con un corpo che non contiene un token |
autenticazione rifiutata — NO [ALERT] Application-specific password required | Sei ancora su AUTH=password: la riga IMAP_AUTH=oauth2 manca |
IMAP_AUTH=oauth2 con IMAP_PASSWORD impostata: togli la password | Hai aggiunto AUTH=oauth2 ma la password è rimasta. Il metodo dev'essere una scelta, non il residuo di una variabile |
OAuth2 senza token: imposta IMAP_ACCESS_TOKEN… | AUTH=oauth2 ma il registry è vuoto: manca OAUTH_CLIENT_ID |
Identità OAuth2 "…" non configurata (disponibili: …) | IMAP_OAUTH_INSTANCE o SMTP_OAUTH_INSTANCE nomina un id che non esiste — un refuso, o OAUTH_INSTANCES senza l'istanza default |
Rotazione
Il client secret e il refresh token si cambiano insieme, rifacendo i passi 5–18. Non c'è nulla da invalidare lato ETL: il fornitore tiene il token in memoria per la durata della run, e la run successiva riparte dal .env.
Revocare il consenso dalla pagina Sicurezza → App di terze parti dell'account Google invalida il refresh token immediatamente: la pipeline successiva fallirà con invalid_grant.
Lo stesso vale, senza che nessuno lo abbia chiesto, per un cambio password della casella: con gli ambiti Gmail il token muore con la vecchia password. Non c'è modo di evitarlo — si può solo sapere che succede, e rifare il consenso subito invece di scoprirlo dal primo processo fallito.

