# Crea un’app OAuth

> 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.

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](/it/docs/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

```bash
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.

```bash
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](#redirect-uri-rules). |
| `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`:

```json title="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

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:

**Client riservato**

```bash
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.

**Client pubblico**

```bash
curl https://api.emailit.com/oauth/token \
  -d grant_type=authorization_code \
  -d client_id="$CLIENT_ID" \
  -d code="$CODE" \
  -d redirect_uri=http://127.0.0.1:53682/callback \
  -d code_verifier="$CODE_VERIFIER"
```

  I client pubblici inviano `client_id` e nessun secret. PKCE dimostra che il flusso è stato avviato dallo stesso client.

```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:

```bash
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:

```bash
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:

```bash
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](/it/docs/account/connected-apps/), 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:

```bash
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](/it/docs/api-reference/errors/):

```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.

## Vedi anche

  - [Workspace e permessi](/it/docs/mcp/workspaces-and-permissions/): Autorizzazioni, durata dei token e revoca.
  - [Riferimento API](/it/docs/api-reference/): Gli endpoint che i tuoi token possono chiamare.
  - [Chiavi API](/it/docs/developers/api-keys/): Per i tuoi script, le chiavi sono più semplici.
  - [Server MCP](/it/docs/mcp/): Il flusso OAuth nella pratica.

---
Fonte: https://emailit.com/it/docs/developers/oauth-apps/
