Vai al contenuto
Docs

Guida

Permetti agli utenti di collegare la tua app al loro workspace Emailit con OAuth 2.1 e PKCE, con registrazione del client, aggiornamento e revoca dei token ed errori.

Aggiornato il 1 ott 2026

Emailit gestisce un server di autorizzazione OAuth 2.1 su https://api.emailit.com. Usalo quando crei un’integrazione, come un CRM, uno strumento no-code o un client AI, che agisce per conto degli utenti di Emailit. I tuoi utenti accedono e approvano l’accesso nel browser, e tu ottieni i token per i workspace che scelgono senza mai gestire le loro chiavi API. È lo stesso flusso che usa il server MCP.

Come funziona

  1. Registra un client con la registrazione dinamica del client, oppure ospita un documento di metadati del client ID.

  2. Indirizza l’utente all’URL di autorizzazione con un code challenge PKCE.

  3. L’utente accede a Emailit, sceglie i workspace che la tua app può usare e approva il permesso (scope) richiesto.

  4. Emailit reindirizza all’URI di reindirizzamento con un codice di autorizzazione monouso.

  5. Scambia il codice con un token di accesso valido 15 minuti e un token di aggiornamento (refresh token).

  6. Chiama l’API con il token di accesso, e aggiornalo quando scade.

Endpoint

Metodo Percorso Scopo
GET /.well-known/oauth-authorization-server Metadati del server di autorizzazione (RFC 8414)
GET /.well-known/oauth-protected-resource Metadati della risorsa protetta per l’API REST
GET /.well-known/oauth-protected-resource/mcp Metadati della risorsa protetta per il server MCP
POST /oauth/register Registrazione dinamica del client (RFC 7591)
GET /oauth/authorize Accesso e consenso nel browser
POST /oauth/token Scambio di un codice o di un token di aggiornamento
POST /oauth/revoke Revoca di un’autorizzazione tramite il suo token di aggiornamento (RFC 7009)
GET /oauth/grants Elenco delle autorizzazioni (dell’utente che ha effettuato l’accesso, o di un workspace con una chiave API)
POST /oauth/grants/:id/revoke Revoca di un’autorizzazione
PUT /oauth/grants/:id/workspaces Modifica dei workspace che un’autorizzazione può usare (da parte dell’utente che ha collegato l’app)

Tutti i percorsi si trovano su https://api.emailit.com.

Individua il server

Terminal
curl https://api.emailit.com/.well-known/oauth-authorization-server
JSON
{
  "issuer": "https://api.emailit.com",
  "authorization_endpoint": "https://api.emailit.com/oauth/authorize",
  "token_endpoint": "https://api.emailit.com/oauth/token",
  "registration_endpoint": "https://api.emailit.com/oauth/register",
  "revocation_endpoint": "https://api.emailit.com/oauth/revoke",
  "jwks_uri": "https://api.emailit.com/.well-known/jwks.json",
  "code_challenge_methods_supported": ["S256"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "response_types_supported": ["code"],
  "token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "client_secret_post"],
  "client_id_metadata_document_supported": true,
  "authorization_response_iss_parameter_supported": true,
  "scopes_supported": ["sending", "full"]
}

I client MCP partono invece dai metadati della risorsa protetta. Una richiesta non autenticata a https://api.emailit.com/mcp restituisce 401 con WWW-Authenticate: Bearer resource_metadata="https://api.emailit.com/.well-known/oauth-protected-resource/mcp", e quel documento rimanda al server di autorizzazione:

JSON
{
  "resource": "https://api.emailit.com/mcp",
  "authorization_servers": ["https://api.emailit.com"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["sending", "full"]
}

Permessi

Permesso Concede
sending Inviare e inoltrare email, e riprogrammare, annullare e ritentare gli invii. Lo stesso accesso di una chiave API di solo invio.
full Ogni endpoint dell’API REST e ogni strumento MCP. Lo stesso accesso di una chiave API con accesso completo.

full include già tutto ciò che consente sending. Richiedi sending se la tua app si limita a inviare, altrimenti full; è accettato anche sending full separato da uno spazio. Se ometti scope, Emailit usa tutti i permessi registrati dal client.

Registra il client

Puoi registrare un client in due modi. Entrambi funzionano con ogni flusso di questa pagina.

Registrazione dinamica del client

Invia una richiesta di registrazione. Non serve autenticazione, e ogni indirizzo IP può registrare fino a 20 client all’ora.

Terminal
curl https://api.emailit.com/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Acme CRM",
    "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "scope": "full",
    "token_endpoint_auth_method": "client_secret_basic",
    "client_uri": "https://crm.acme.com",
    "logo_uri": "https://crm.acme.com/logo.png"
  }'
Campo Obbligatorio Descrizione
client_name Sì Mostrato nella pagina di consenso. Fino a 200 caratteri.
redirect_uris Sì Da 1 a 10 URI di reindirizzamento. Vedi Regole per gli URI di reindirizzamento.
grant_types No Deve includere authorization_code; può includere refresh_token. Il valore predefinito li comprende entrambi.
response_types No Solo code.
scope No Permessi separati da spazi. Il valore predefinito è sending full.
token_endpoint_auth_method No none (predefinito) per i client pubblici come app desktop, mobile e browser; client_secret_basic o client_secret_post per le app lato server.
client_uri No La home page della tua app.
logo_uri No Logo mostrato nella pagina di consenso.

La risposta 201 riporta i metadati e aggiunge un client_id. I client riservati ricevono anche un client_secret, mostrato una sola volta:

JSON
{
  "client_id": "3e8a1f52-7c4b-4d29-8e6f-1a5b9c0d2e73",
  "client_id_issued_at": 1790847000,
  "client_name": "Acme CRM",
  "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "full",
  "token_endpoint_auth_method": "client_secret_basic",
  "client_uri": "https://crm.acme.com",
  "logo_uri": "https://crm.acme.com/logo.png",
  "client_secret": "Ky7WI2z5HxoT6q1z19tuiIev_U1zX0_FKhIIfh0OBco",
  "client_secret_expires_at": 0
}

client_secret_expires_at è sempre 0: i secret non scadono. Ogni client, pubblico o riservato, deve usare PKCE.

Documento di metadati del client ID

Se la tua app può ospitare un file JSON statico, puoi saltare la registrazione. Pubblica un documento a un URL HTTPS e usa quell’URL come client_id:

https://crm.acme.com/oauth/client.json
{
  "client_id": "https://crm.acme.com/oauth/client.json",
  "client_name": "Acme CRM",
  "client_uri": "https://crm.acme.com",
  "logo_uri": "https://crm.acme.com/logo.png",
  "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_method": "none",
  "scope": "full"
}
  • Il client_id nel documento deve essere esattamente uguale all’URL del documento.
  • token_endpoint_auth_method deve essere none. Sono client pubblici che si basano su PKCE.
  • Gli URI di reindirizzamento HTTPS devono trovarsi sullo stesso host del documento.
  • Emailit scarica il documento durante l’autorizzazione, con un timeout di 5 secondi e senza seguire i reindirizzamenti, e lo tiene in cache per 5 minuti.
  • La pagina di consenso mostra l’host del documento, ad esempio crm.acme.com, al posto di client_name.

I client AI usano questo metodo: ChatGPT, Claude e Grok si identificano con i propri documenti di metadati del client ID, quindi si collegano senza registrarsi prima. Per Grok, Emailit accetta anche i reindirizzamenti a console.x.ai.

Regole per gli URI di reindirizzamento

  • Gli URI https:// sono consentiti.
  • http:// è consentito solo per gli host di loopback: 127.0.0.1, localhost e [::1]. Per gli URI di loopback la porta può essere diversa al momento dell’autorizzazione, ma host, percorso e query devono corrispondere. localhost e 127.0.0.1 sono host diversi.
  • Gli schemi a uso privato come cursor://, vscode:// o com.acme.crm:// sono consentiti per le app native.
  • Gli URI file, ftp, data, javascript, blob, about e vbscript, e qualsiasi URI con un #fragment, vengono rifiutati.
  • A parte le porte di loopback, il redirect_uri che invii deve corrispondere esattamente a uno di quelli registrati.

Indirizza l’utente a Emailit

Crea un verifier e un challenge PKCE, e uno state casuale, per ogni autorizzazione:

JavaScript
import { createHash, randomBytes } from 'node:crypto';

const codeVerifier = randomBytes(32).toString('base64url');
const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
const state = randomBytes(16).toString('base64url');
// Store codeVerifier and state in the user's session.

Poi reindirizza il browser dell’utente all’endpoint di autorizzazione:

Text
https://api.emailit.com/oauth/authorize
  ?response_type=code
  &client_id=3e8a1f52-7c4b-4d29-8e6f-1a5b9c0d2e73
  &redirect_uri=https%3A%2F%2Fcrm.acme.com%2Foauth%2Femailit%2Fcallback
  &scope=full
  &state=Jq3k9V0n2xR7bLm1
  &code_challenge=zsrvXVr2pbLDHbccAx_NY9osQ6nwqTnDZlIeFWKQOsg
  &code_challenge_method=S256
  &resource=https%3A%2F%2Fapi.emailit.com
Parametro Obbligatorio Descrizione
response_type Sì code
client_id Sì Il tuo client ID, o l’URL del tuo documento di metadati.
redirect_uri Sì Uno dei tuoi URI di reindirizzamento registrati.
code_challenge Sì SHA-256 del code verifier, codificato in Base64url.
code_challenge_method Consigliato S256. plain non è supportato.
scope Consigliato sending o full. Deve essere un permesso registrato dal client.
state Consigliato Un valore casuale che controlli nella callback.
resource No La risorsa che vuoi chiamare (RFC 8707): https://api.emailit.com per l’API REST o https://api.emailit.com/mcp per MCP. I token funzionano comunque su entrambe.

Apri questo URL nel browser dell’utente. Non richiederlo dal tuo backend.

Cosa vede l’utente

  1. Accesso a Emailit. L’utente inserisce email e password. Se usa un’app di autenticazione per l’autenticazione a due fattori, inserisce poi il suo codice o un codice di recupero.
  2. Scelta dei workspace. La pagina mostra il tuo logo, il nome del client (o l’host del documento di metadati) e i permessi richiesti. L’utente sceglie All my workspaces, che include i workspace che creerà o a cui si unirà in seguito, oppure Only these workspaces con quelli che spunta, e sceglie il workspace in cui la tua app inizia.
  3. Consenso all’accesso. L’utente seleziona Allow access o Deny.

Un’autorizzazione può coprire più workspace. L’utente può modificare l’elenco in seguito nel pannello in Account > Connected apps, e la tua app vede la modifica alla richiesta successiva.

Dopo l’approvazione, Emailit reindirizza all’URI di reindirizzamento:

Text
https://crm.acme.com/oauth/emailit/callback?code=XvRKyG5Pkplrd3xkRNaEayKkpkebEMdF&state=Jq3k9V0n2xR7bLm1&iss=https%3A%2F%2Fapi.emailit.com

Controlla che state corrisponda al valore che hai salvato e che iss sia https://api.emailit.com. Il codice è valido 10 minuti e si può usare una sola volta. Se l’utente seleziona Deny, il reindirizzamento contiene invece error=access_denied.

Scambia il codice con i token

Invia una richiesta POST all’endpoint dei token come application/x-www-form-urlencoded (è accettato anche JSON). Invia lo stesso redirect_uri e il code verifier originale:

Terminal
curl https://api.emailit.com/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri=https://crm.acme.com/oauth/emailit/callback \
  -d code_verifier="$CODE_VERIFIER"

Con client_secret_basic, invia client ID e secret nell’header Authorization: Basic. Con client_secret_post, invia invece client_id e client_secret come campi del modulo. Non inviare il secret in entrambi i modi.

JSON
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im9hdXRoLWhzMjU2In0…",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "XvRKyG5Pkplrd3xkRNaEayKkpkebEMdFn0h1VIuqixAOXybMtJfK8w",
  "scope": "full"
}

Il token di accesso dura 15 minuti (expires_in è in secondi). Trattalo come una stringa opaca: non analizzarlo e non fare affidamento sul suo contenuto.

Chiama l’API

Usa il token di accesso come token Bearer sull’API REST e sul server MCP, esattamente come una chiave API:

Terminal
curl https://api.emailit.com/v2/domains \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Le richieste REST vengono eseguite nel workspace predefinito dell’autorizzazione: quello in cui l’utente ha scelto di iniziare, o quello impostato in seguito. Gli strumenti MCP possono anche agire su un altro workspace consentito con l’argomento workspace. Ogni richiesta usa il permesso concesso e il ruolo dell’utente nel workspace, quindi una richiesta fuori dal permesso, o una route riservata agli Admin chiamata da un membro con ruolo Member, restituisce 403. Se l’utente lascia un workspace, anche l’autorizzazione lo perde.

Aggiorna i token

Prima che il token di accesso scada, o quando una richiesta restituisce 401, ottienine uno nuovo:

Terminal
curl https://api.emailit.com/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token="$REFRESH_TOKEN"
  • Rotazione. Ogni aggiornamento restituisce un nuovo refresh_token valido 60 giorni. Salvalo e scarta quello vecchio. Finché aggiorni entro 60 giorni, la connessione non scade.
  • Rilevamento del riutilizzo. Se un vecchio token di aggiornamento viene usato di nuovo più di un minuto dopo essere stato sostituito, Emailit restituisce invalid_grant (Refresh token reuse detected) e revoca l’intera autorizzazione. L’utente deve quindi autorizzare di nuovo. Esegui gli aggiornamenti in sequenza, così due worker non usano mai lo stesso token di aggiornamento.
  • Permesso più ristretto. Puoi passare scope per ottenere un token di accesso con meno permessi dell’autorizzazione. Non puoi chiederne di più.

Revoca un’autorizzazione

Quando un utente scollega Emailit dalla tua app, revoca l’autorizzazione con il suo token di aggiornamento:

Terminal
curl https://api.emailit.com/oauth/revoke \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d token="$REFRESH_TOKEN"

L’endpoint autentica il client allo stesso modo dell’endpoint dei token e restituisce sempre 200 con un corpo vuoto. Revocare il token di aggiornamento revoca l’intera autorizzazione: i suoi token di accesso smettono di funzionare alla richiesta successiva. I token di accesso non si possono revocare singolarmente; token_type_hint=access_token restituisce invalid_request.

Gli utenti possono anche revocare l’accesso della tua app dal proprio lato, nel pannello in App collegate, o con l’API delle autorizzazioni descritta qui sotto.

Gestisci le autorizzazioni

L’API delle autorizzazioni elenca e modifica le autorizzazioni dal lato dell’utente. Accetta la sessione del pannello dell’utente o una chiave API con accesso completo:

Chiamante GET /oauth/grants POST /oauth/grants/:id/revoke PUT /oauth/grants/:id/workspaces
L’utente che ha collegato l’app Le sue autorizzazioni in tutti i workspace Revoca l’intera autorizzazione Modifica i workspace dell’autorizzazione
Chiave API con accesso completo Le autorizzazioni che includono il workspace della chiave Rimuove il workspace della chiave dall’autorizzazione; l’autorizzazione viene revocata quando non resta alcun workspace Non consentito (403)

Ogni autorizzazione nell’elenco ha questa forma:

JSON
{
  "id": "9b1f6c2e-4d3a-4f8e-9a7b-2c5d8e1f0a3b",
  "client_id": "https://chatgpt.com/oauth/client.json",
  "client": { "name": "ChatGPT", "uri": "https://chatgpt.com", "logo_uri": null },
  "default_workspace": { "id": "w3f9a1c2e", "name": "Acme" },
  "workspace_access": "selected",
  "workspaces": [{ "id": "w3f9a1c2e", "name": "Acme" }],
  "scopes": ["sending", "full"],
  "resource": "https://api.emailit.com/mcp",
  "created_at": "2026-10-01T09:30:12Z",
  "revoked_at": null,
  "revoked_reason": null
}

Per modificare i workspace di un’autorizzazione, invia access (all o selected), workspace_ids per selected e un default_workspace_id facoltativo, che deve essere uno dei workspace consentiti:

Terminal
curl -X PUT https://api.emailit.com/oauth/grants/9b1f6c2e-4d3a-4f8e-9a7b-2c5d8e1f0a3b/workspaces \
  -H "Authorization: Bearer $DASHBOARD_SESSION" \
  -H "Content-Type: application/json" \
  -d '{ "access": "selected", "workspace_ids": ["w3f9a1c2e"], "default_workspace_id": "w3f9a1c2e" }'

L’utente deve essere membro di ogni workspace che seleziona. Un’autorizzazione revocata non si può modificare (409); l’app deve collegarsi di nuovo.

Errori

Gli endpoint OAuth restituiscono gli errori nel formato OAuth, non nel formato degli errori dell’API REST:

JSON
{
  "error": "invalid_grant",
  "error_description": "Authorization code has expired"
}
Errore Stato Quando
invalid_request 400 Un parametro manca o non è valido, il client è sconosciuto al momento dell’autorizzazione, il redirect_uri non è registrato, code_challenge_method non è S256, oppure un client secret è stato inviato sia nell’header sia nel corpo.
invalid_client 401 L’endpoint dei token o di revoca non riesce ad autenticare il client: client sconosciuto, secret mancante o errato.
invalid_grant 400 Il codice non è valido, è già stato usato o è scaduto; il redirect_uri o il verifier PKCE non corrispondono; oppure il token di aggiornamento non è valido, è scaduto, è stato revocato o riutilizzato.
invalid_scope 400 Il permesso non è supportato, non è stato registrato per il client o, in fase di aggiornamento, supera quanto concesso dall’autorizzazione.
unsupported_response_type 400 response_type non è code.
unsupported_grant_type 400 grant_type non è authorization_code né refresh_token.
access_denied Reindirizzamento L’utente ha selezionato Deny.
too_many_requests 429 Più di 20 registrazioni all’ora da uno stesso indirizzo IP.
server_error 500 Si è verificato un problema dal lato di Emailit. Riprova.

Finché client_id e redirect_uri non sono convalidati, gli errori di autorizzazione vengono mostrati come JSON nel browser e mai reindirizzati. Dopo, gli errori vengono reindirizzati alla tua callback (con error, error_description, state e iss) solo per gli URI di reindirizzamento di loopback e a uso privato e per i client con documento di metadati; gli altri client ricevono una pagina di errore JSON. Il Deny di un utente viene sempre reindirizzato.

Checklist di sicurezza

  • Tieni client_secret e i token di aggiornamento sul tuo server, cifrati a riposo. Non includere mai un client secret in un’app mobile, desktop o browser; usa invece un client pubblico con PKCE.
  • Genera un nuovo state e un nuovo code verifier per ogni autorizzazione, e controlla state e iss nella callback.
  • Registra URI di reindirizzamento esatti. Non usare open redirect come callback.
  • Salva ogni autorizzazione con il workspace a cui appartiene, e gestisci invalid_grant chiedendo all’utente di collegarsi di nuovo.
  • Richiedi sending se invii solo email.
Autorizzazioni, durata dei token e revoca.
Gli endpoint che i tuoi token possono chiamare.
Per i tuoi script, le chiavi sono più semplici.
Il flusso OAuth nella pratica.

Questa pagina ti è stata utile?

Grazie del feedback.

Grazie, leggiamo ogni messaggio.