> This page is for Piattaforma, version V4 (default).
> For other versions, use one of these documentation indexes:
> - V4 (default): https://next.developer.frame.io/platform/v4/llms.txt
> - V4 sperimentale: https://next.developer.frame.io/platform/v4-experimental/llms.txt
> - Versione precedente: https://next.developer.frame.io/platform/v2/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://next.developer.frame.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://next.developer.frame.io/_mcp/server.

# SDK TypeScript Frame.io — Guida all'autenticazione

Questa guida spiega come eseguire l'autenticazione con l'API Frame.io usando l'**SDK TypeScript Frame.io** (`frameio`). L'API V4 Frame.io usa [Adobe Identity Management Service (IMS)](https://developer.adobe.com/developer-console/docs/guides/authentication/), la piattaforma di identità OAuth 2.0 di Adobe. Questo è un riferimento autonomo per sviluppatori TypeScript/JavaScript. Tutti gli esempi di codice e i flussi seguenti sono solo per il pacchetto `frameio`.

---

## Tipi di autenticazione nell'SDK TypeScript

L'SDK TypeScript supporta quattro classi di autenticazione OAuth, oltre all'uso diretto di token:

| Metodo                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | Caso d'uso                                                              | Interazione utente? | Richiede un secret del client? |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------- | ------------------------------ |
| **Token statico**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | Script rapidi, test o se hai già un token                               | No                  | No                             |
| **Server-to-server**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Servizi di backend, cron job, automazione                               | No                  | Sì                             |
| **Web app**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | App lato server (Express, Fastify, Next.js)                             | Sì                  | Sì                             |
| **SPA (PKCE)**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | App browser che non possono archiviare un secret                        | Sì                  | No                             |
| **App nativa (PKCE)**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | App desktop/mobile con reindirizzamenti dello schema URI personalizzato | Sì                  | No                             |
| **Server-to-server** consente alla tua app di fungere da account servizio senza interazione utente.È disponibile solo per gli account Frame.io V4 amministrati tramite [Adobe Admin Console](https://adminconsole.adobe.com/).**Web app** e **SPA** consentono all'app di agire da utente specifico.Entrambi utilizzano Adobe IMS in background: l'utente autorizza la tua app e l'SDK scambia il codice risultante con i token.L'SDK TypeScript gestisce per te il flusso IMS `/authorize/v2` e `/token/v3`.Per Web app hai bisogno di un secret del client; per SPA usi invece [PKCE](https://datatracker.ietf.org/doc/html/rfc7636).**App nativa** segue lo stesso flusso PKCE di SPA, ma utilizza l'URI di reindirizzamento `adobe+<hash>://callback</hash>` che Adobe assegna alle credenziali della tua app nativa. In questo modo, la tua applicazione può intercettare il reindirizzamento a livello di sistema operativo dopo l'autorizzazione. |                                                                         |                     |                                |

---

## Utenti dell'account di servizio

Quando usi l'autenticazione server-to-server, l'applicazione agisce come **utente dell'account di servizio**, un tipo di account distinto che può eseguire azioni per conto del servizio. Questi sono visibili agli altri utenti in Frame.io: quando un account di servizio esegue un'azione, il nome viene visualizzato nell'interfaccia utente. Puoi concedere e revocare l'accesso dell'account di servizio tramite [Adobe Admin Console](https://adminconsole.adobe.com/) e [Developer Console](https://developer.adobe.com/console). I nomi degli account di servizio vengono gestiti dall'interfaccia utente Frame.io. Per impostazione predefinita, la prima connessione S2S viene denominata **Service Account User**, la seconda **Service Account User 2** e così via.

> **Info**
>
> Consulta [Automatizza la configurazione usando il supporto server-to-server di Frame.io](https://helpx.adobe.com/enterprise/using/automate-using-frame-io.html) per ulteriori informazioni.

---

## Avvio rapido

### Prerequisiti

1. **Credenziali** da [Adobe Developer Console](https://developer.adobe.com/console):

* **ID client**: richiesto per tutti i flussi OAuth - **Secret client**: richiesto per i flussi server-to-server e web app - **URI di reindirizzamento**: richiesto per i flussi web app, SPA e app nativa; deve essere registrato nel progetto Adobe

2. **Installa l'SDK:**

```bash
npm install frameio
```

### Scelta di un metodo

* **Non è coinvolto nessun utente?** Usa **server-to-server** (`ServerToServerAuth`).
* **È coinvolto un utente e puoi archiviare un secret?** Usa **web app** (`WebAppAuth`).
* **È coinvolto un utente, ma non puoi archiviare un secret?** Utilizza **SPA** (`SPAAuth`) per le app basate su browser, o **App nativa** (`NativeAppAuth`) per le app desktop/mobile.

---

## Token di accesso

Se hai già un token di accesso (da un altro sistema OAuth o da uno scambio precedente, ad esempio tramite il nostro [Explorer di API](/platform/api-reference/accounts/index?explorer=true)) puoi passarlo direttamente:

```typescript
import { FrameioClient } from "frameio";

const client = new FrameioClient({ token: "YOUR_ACCESS_TOKEN" });
```

Questo è l'approccio più semplice, ma il token scadrà e l'SDK non lo aggiornerà automaticamente.

### Token sviluppatore legacy

Per gli account migrati a V4 e non ancora amministrati tramite [Adobe Admin Console](https://adminconsole.adobe.com/), puoi continuare a utilizzare i token sviluppatore legacy dal [sito per sviluppatori di Frame.io](https://developer.frame.io/app/tokens). Devi includere l'intestazione `x-frameio-legacy-token-auth` e impostarla su `true`:

```typescript
import { FrameioClient } from "frameio";

const client = new FrameioClient({
    token: "YOUR_LEGACY_DEVELOPER_TOKEN",
    headers: { "x-frameio-legacy-token-auth": "true" },
});
```

I token sviluppatore legacy non scadono, ma sono un meccanismo di transizione. Per le nuove integrazioni e i carichi di lavoro di produzione, consigliamo di utilizzare uno dei flussi OAuth 2.0 indicati di seguito. Consulta la [guida alla migrazione](/platform/docs/resources/migration#authentication) per i dettagli.

---

## Server-to-server (credenziali client)

Utilizza questo metodo per servizi e script backend che necessitano dell'accesso a Frame.io senza interazioni da parte dell'utente. Questo flusso è disponibile solo per gli account Frame.io V4 amministrati tramite [Adobe Admin Console](https://adminconsole.adobe.com/). La tua applicazione si autentica come [utente dell'account di servizio](#service-account-users) senza intervento umano.

```typescript
import { FrameioClient, ServerToServerAuth } from "frameio";

const auth = new ServerToServerAuth({
    clientId: "YOUR_CLIENT_ID",
    clientSecret: "YOUR_CLIENT_SECRET",
});

const client = new FrameioClient({ token: () => auth.getToken() });
```

Ecco fatto. `auth.getToken()` è una funzione asincrona che l'SDK richiama ad ogni richiesta. Se il token corrente è ancora valido, restituisce immediatamente il risultato. Se sta per scadere, ne recupera prima uno nuovo, in modo completamente trasparente.

### Come funziona

Le tue credenziali client (ID client + secret) **non scadono mai**. Basta ruotarle manualmente per garantire l'integrità della sicurezza. S2S offre un accesso API permanente e ininterrotto senza alcun intervento manuale.

Dietro le quinte:

1. Alla prima chiamata API, `getToken()` richiede un nuovo token di accesso da Adobe IMS utilizzando la concessione `client_credentials`.
2. Il token viene memorizzato nella cache in memoria. I singoli token di accesso scadono (in genere 24 ore), ma questo viene gestito automaticamente.
3. Quando un token memorizzato nella cache si trova entro il buffer di aggiornamento (predefinito: 60 secondi prima della scadenza), l'SDK ne recupera automaticamente uno nuovo utilizzando le stesse credenziali client.
4. Non sono coinvolti token di aggiornamento. Le credenziali client stesse sono il secret di lunga durata e possono sempre essere utilizzate per generare un nuovo token di accesso.

### Autenticazione esplicita

Se desideri recuperare il token in modo proattivo (ad esempio, per generare rapidamente un errore in caso di credenziali errate all'avvio):

```typescript
const auth = new ServerToServerAuth({ clientId: "...", clientSecret: "..." });
await auth.authenticate(); // throws AuthenticationError if credentials are invalid
const client = new FrameioClient({ token: () => auth.getToken() });
```

---

## Web app (codice di autorizzazione)

Utilizza questo metodo per applicazioni lato server in cui gli utenti accedono con il proprio Adobe ID. Questo flusso richiede un client secret, che deve essere archiviato in modo sicuro sul server.

#### Reindirizza l'utente ad Adobe IMS

```typescript
    import { WebAppAuth } from "frameio";
    import crypto from "crypto";

    const auth = new WebAppAuth({
        clientId: "YOUR_CLIENT_ID",
        clientSecret: "YOUR_CLIENT_SECRET",
        redirectUri: "https://yourapp.com/callback",
    });

    // Generate a cryptographically random state value to prevent CSRF attacks
    const state = crypto.randomBytes(32).toString("hex");

    const authorizationUrl = auth.getAuthorizationUrl({ state });
    // Store `state` in the user's session, then redirect them to `authorizationUrl`
```

#### Gestisci il callback

Quando Adobe IMS reindirizza l'utente al tuo `redirectUri`, estrai i parametri `code` e `state`. Verifica che lo stato corrisponda a quello che hai archiviato, quindi scambia il codice con i token:

```typescript
    // In your callback handler (e.g. an Express route):
    await auth.exchangeCode(req.query.code as string);
```

Questa operazione scambia il codice di autorizzazione con un token di accesso e un token di aggiornamento, archiviandoli entrambi internamente.

#### Usa il client

```typescript
    import { FrameioClient } from "frameio";

    const client = new FrameioClient({ token: () => auth.getToken() });
```

Ecco fatto. Da questo punto in poi, `getToken()` gestisce automaticamente il ciclo di vita del token. Quando il token di accesso si avvicina alla scadenza, l'SDK usa il token di aggiornamento per ottenerne uno nuovo. Non è richiesta alcuna interazione da parte dell'utente.

### Esempio completo di Express

```typescript
import crypto from "crypto";
import express from "express";
import session from "express-session";
import { FrameioClient, WebAppAuth } from "frameio";

const auth = new WebAppAuth({
    clientId: "YOUR_CLIENT_ID",
    clientSecret: "YOUR_CLIENT_SECRET",
    redirectUri: "http://localhost:3000/callback",
});

const app = express();
app.use(session({ secret: crypto.randomBytes(32).toString("hex"), resave: false, saveUninitialized: false }));

app.get("/login", (req, res) => {
    const state = crypto.randomBytes(32).toString("hex");
    (req.session as any).oauthState = state;
    res.redirect(auth.getAuthorizationUrl({ state }));
});

app.get("/callback", async (req, res) => {
    if (req.query.state !== (req.session as any).oauthState) {
        return res.status(403).send("Invalid state parameter");
    }

    await auth.exchangeCode(req.query.code as string);

    const client = new FrameioClient({ token: () => auth.getToken() });
    const accounts = await client.accounts.index();
    res.json(accounts);
});

app.listen(3000);
```

---

## App a singola pagina/PKCE (codice di autorizzazione + PKCE)

Utilizza questa opzione per applicazioni basate su browser, applicazioni desktop o strumenti CLI che non possono archiviare in modo sicuro un secret del client. Questo flusso utilizza [PKCE (RFC 7636)](https://datatracker.ietf.org/doc/html/rfc7636) per proteggere lo scambio del codice di autorizzazione.

#### Genera l'URL di autorizzazione

```typescript
    import { SPAAuth } from "frameio";

    const auth = new SPAAuth({
        clientId: "YOUR_CLIENT_ID",
        redirectUri: "https://yourapp.com/callback",
    });

    const state = crypto.randomUUID();
    const result = await auth.getAuthorizationUrl({ state });
    // result.url            -> redirect the user here
    // result.codeVerifier   -> store this securely until the callback
```

`getAuthorizationUrl` restituisce un `AuthorizationUrlResult` che contiene l'URL completo (con il `code_challenge` PKCE incorporato) e il `codeVerifier` necessario per il passaggio successivo.

#### Scambia il codice con il verificatore

Quando l'utente viene reindirizzato:

```typescript
    await auth.exchangeCode({
        code: "CODE_FROM_CALLBACK",
        codeVerifier: result.codeVerifier,
    });
```

#### Usa il client

```typescript
    import { FrameioClient } from "frameio";

    const client = new FrameioClient({ token: () => auth.getToken() });
```

Ecco fatto. L'aggiornamento funziona come per web app: l'SDK usa automaticamente il token di aggiornamento. La differenza è che, durante l'aggiornamento, non viene inviato alcun secret del client, poiché il flusso SPA è progettato per client pubblici.

> **Warning**
>
> Il `codeVerifier` deve essere archiviato in modo sicuro lato client tra la richiesta di autorizzazione e lo scambio del codice. Usa `sessionStorage` o l'equivalente nelle app basate su browser.

---

## App nativa (codice di autorizzazione + PKCE)

Usa questo metodo per le applicazioni desktop e mobile. Quando crei una credenziale di app nativa in [Adobe Developer Console](https://developer.adobe.com/console), Adobe ti assegna un URI di reindirizzamento nel formato `adobe+<hash>://callback</hash>`: devi registrare l'applicazione per gestire quello schema URI personalizzato a livello di sistema operativo. Sono supportati anche i reindirizzamenti loopback (`http://127.0.0.1:<port>/callback</port>`) per lo sviluppo locale. Il flusso è identico a SPA: usa PKCE senza secret del client.

```typescript
import { NativeAppAuth } from "frameio";

const auth = new NativeAppAuth({
    clientId: "YOUR_CLIENT_ID",
    redirectUri: "adobe+abc123def456://callback", // from your Adobe Developer Console Native App credential
    // Also supports loopback: "http://127.0.0.1:8080/callback"
});

const { url, codeVerifier } = await auth.getAuthorizationUrl({
    state: crypto.randomUUID(),
});

// Open system browser to `url`
// Listen for redirect on your custom URI scheme or loopback server

await auth.exchangeCode({ code: "CODE_FROM_REDIRECT", codeVerifier });
const client = new FrameioClient({ token: () => auth.getToken() });
```

### Regole per l'URI di reindirizzamento

Adobe applica le regole per l'URI di reindirizzamento in due punti: quando registri le credenziali in [Adobe Developer Console](https://developer.adobe.com/console) e quando il parametro `redirect_uri` raggiunge l'endpoint `/authorize/v2`. Il valore che passi a `redirectUri` in questo SDK deve corrispondere a uno dei pattern dell'URI di reindirizzamento che hai registrato nelle credenziali, altrimenti Adobe reinvia all'URI di reindirizzamento predefinito delle credenziali.

* Le credenziali per **web app** e **SPA** richiedono HTTPS.
* Le credenziali per **app nativa** usano un reindirizzamento non HTTPS, di solito l'URI `adobe+<hash>://callback</hash>` mostrato in Developer Console per le credenziali.

Consulta [Adobe Developer Console](https://developer.adobe.com/console) per i pattern esatti accettati per le tue credenziali.

> **Note**
>
> L'SDK Python non include una classe di credenziali per app native, poiché Python non ha un modo standard per registrare gestori di schemi
>
> URI personalizzati. L'SDK TypeScript supporta tutti e quattro i tipi di credenziali, inclusa l'app nativa.

---

## Aggiornamento manuale del token

Per i flussi di web app, SPA e app nativa, l'SDK aggiorna automaticamente i token tramite `getToken()`. Se hai bisogno di un controllo esplicito, puoi chiamare direttamente `refresh()`:

```typescript
await auth.refresh(); // fetches a new access token using the refresh token
```

Questo è utile quando vuoi forzare un aggiornamento prima di un'operazione critica anziché affidarti al buffer di aggiornamento automatico.

`refresh()` è disponibile su `WebAppAuth`, `SPAAuth` e `NativeAppAuth`. Genera `ConfigurationError` se non è disponibile alcun token di aggiornamento (ovvero devi prima chiamare `exchangeCode()`). `ServerToServerAuth` non ha un metodo `refresh()`. Devi usare `authenticate()` per recuperare un nuovo token tramite le credenziali client.

---

## Persistenza del token

Tutte le classi di autenticazione supportano `exportTokens()` e `importTokens()` per mantenere lo stato del token durante i riavvii. Questo è particolarmente importante per i flussi di web app, SPA e app nativa, poiché i token di accesso e di aggiornamento risiedono in memoria per impostazione predefinita. Se l'applicazione si riavvia, gli utenti dovrebbero autenticarsi nuovamente, a meno che non li rendi persistenti. Per gli scenari server-to-server, la persistenza è facoltativa (le credenziali client possono sempre generare un nuovo token), ma l'importazione di un token memorizzato in cache evita un round-trip aggiuntivo all'avvio.

### Esportazione e importazione

```typescript
// After exchangeCode(), save the token state
const tokenData = auth.exportTokens();
// tokenData is: { access_token: "...", refresh_token: "...", expires_at: 1234567890.0 }
// Save it to your database, file, or secret store

// On next startup, restore it
auth.importTokens(tokenData);
const client = new FrameioClient({ token: () => auth.getToken() });
// The SDK will automatically refresh if the token is near expiry
```

> **Warning**
>
> Archivia i token esportati in modo sicuro. Contengono i token di accesso e di aggiornamento che concedono l'accesso all'API. Evita di scrivere i token
>
> in file di testo normale in produzione.

### Persistenza automatica con `onTokenRefreshed`

Per rendere automaticamente persistenti i token ogni volta che vengono aggiornati, usa il callback `onTokenRefreshed`:

```typescript
import fs from "fs/promises";

const TOKEN_FILE = "tokens.json";

const auth = new WebAppAuth({
    clientId: "...",
    clientSecret: "...",
    redirectUri: "...",
    onTokenRefreshed: (tokens) => {
        fs.writeFile(TOKEN_FILE, JSON.stringify(tokens));
    },
});

// On startup, restore if available
try {
    const saved = JSON.parse(await fs.readFile(TOKEN_FILE, "utf-8"));
    auth.importTokens(saved);
} catch {
    // No saved tokens — user will need to authenticate
}
```

Il callback riceve la stessa forma di `exportTokens()` e viene attivato dopo ogni aggiornamento riuscito del token.

---

## Revoca dei token

Per disconnettere un utente e invalidare i suoi token con Adobe IMS:

```typescript
await auth.revoke();
```

Vengono inviate due richieste di revoca a Adobe IMS: una per il token di accesso e una per il token di aggiornamento, in parallelo. Lo stato del token locale viene cancellato completamente.Per i client riservati (`WebAppAuth`), le richieste di revoca utilizzano l'autenticazione HTTP Basic; per i client pubblici (`SPAAuth`, `NativeAppAuth`), il `client_id` viene inviato come parametro di query. Gli errori di revoca vengono registrati, ma non restituiti. Dopo la revoca, l'utente dovrà autenticarsi di nuovo.

---

## Gestione degli errori

Tutti gli errori di autenticazione ereditano da `FrameioAuthError`, quindi puoi intercettarli in modo generale o gestire casi specifici:

```typescript
import {
    FrameioAuthError,
    AuthenticationError,
    TokenExpiredError,
    ConfigurationError,
    NetworkError,
    RateLimitError,
} from "frameio";

try {
    await auth.exchangeCode("...");
} catch (error) {
    if (error instanceof TokenExpiredError) {
        // The refresh token has expired; redirect the user to sign in again
    } else if (error instanceof AuthenticationError) {
        // Token exchange failed
        console.error(`Error: ${error.errorCode} - ${error.errorDescription}`);
    } else if (error instanceof NetworkError) {
        // Timeout or connection failure (after retries)
    } else if (error instanceof RateLimitError) {
        // 429 from Adobe IMS; retry after error.retryAfter seconds
    } else if (error instanceof FrameioAuthError) {
        // Catch-all for any other auth error
    }
}
```

### Riferimento dell'errore

| Eccezione             | Quando viene generato                                                                                                         |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `ConfigurationError`  | Configurazione mancante o non valida (ad esempio `clientId` vuoto, URI di reindirizzamento non HTTPS, `imsBaseUrl` non HTTPS) |
| `AuthenticationError` | Scambio o aggiornamento del token rifiutato da Adobe IMS (con `.errorCode` ed `.errorDescription`)                            |
| `TokenExpiredError`   | Il token di aggiornamento stesso è scaduto; l'utente deve eseguire nuovamente l'autenticazione                                |
| `NetworkError`        | Timeout HTTP o errore di connessione dopo tutti i tentativi                                                                   |
| `RateLimitError`      | Adobe IMS ha restituito un errore 429; controlla `.retryAfter` per indicazioni sul backoff                                    |
| `PKCEError`           | Disponibile per l'utente consumer nei flussi PKCE; non generato internamente dall'SDK                                         |

### Gestione dei token di aggiornamento scaduti in produzione

> **Warning**
>
> Per i flussi di web app, SPA e app nativa, il token di aggiornamento scadrà.Quando ciò accade, `getToken()` genera `TokenExpiredError`. Devi intercettare questo errore e reindirizzare l'utente attraverso il flusso di autorizzazione.

```typescript
import { TokenExpiredError } from "frameio";

try {
    const client = new FrameioClient({ token: () => auth.getToken() });
    const assets = await client.files.list({ projectId: "..." });
} catch (error) {
    if (error instanceof TokenExpiredError) {
        // Clear persisted tokens and redirect user to login
        await auth.revoke();
        return res.redirect("/login");
    }
}
```

---

## Riferimento della configurazione

Questi parametri hanno impostazioni predefinite sensibili e raramente devono essere impostati. Se devi personalizzarne il comportamento (ad esempio puntando a un IMS temporaneo, inserendo un `fetch` personalizzato, regolando i timeout o configurando un logger), passa qualsiasi di questi valori come parametri opzionali durante la creazione della classe di autenticazione:

| Parametro          | Predefinito                                       | Descrizione                                                                                                                                                                                                                        |
| ------------------ | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scopes`           | Impostazioni predefinite specifiche per il flusso | Ambiti OAuth separati da spazio. S2S ha come impostazione predefinita `openid AdobeID frame.s2s.all`; i flussi rivolti all'utente hanno come impostazione predefinita `openid email profile offline_access additional_info.roles`. |
| `imsBaseUrl`       | `https://ims-na1.adobelogin.com`                  | URL di base di Adobe IMS. Sostituisce gli ambienti temporanei o non di produzione. Deve utilizzare HTTPS.                                                                                                                          |
| `fetch`            | `globalThis.fetch`                                | Implementazione `fetch` personalizzata per proxy, mTLS o gestione HTTP personalizzata.                                                                                                                                             |
| `timeout`          | `30000`                                           | Timeout della richiesta HTTP in millisecondi per le chiamate endpoint del token.                                                                                                                                                   |
| `maxRetries`       | `2`                                               | Numero massimo di nuovi tentativi per errori temporanei (5xx, timeout). I nuovi tentativi per limite di frequenza (429) vengono tracciati separatamente.                                                                           |
| `refreshBuffer`    | `60`                                              | Secondi prima della scadenza del token per attivare l'aggiornamento proattivo.                                                                                                                                                     |
| `onTokenRefreshed` | `undefined`                                       | Callback attivato dopo ogni aggiornamento riuscito del token. Riceve un oggetto con `access_token`, `refresh_token` ed `expires_at`.                                                                                               |
| `logger`           | Non operativo (silenzioso)                        | Istanza del logger con metodi `debug`, `info`, `warn`, `error` (ad es. `console`, `pino`, `winston`).                                                                                                                              |

### Ambienti temporanei

Punta a un'istanza Adobe IMS temporanea sostituendo `imsBaseUrl`. L'SDK esporta anche `DEFAULT_IMS_BASE_URL` (`https://ims-na1.adobelogin.com`) se devi fare riferimento al valore di produzione a livello programmatico.

```typescript
const auth = new ServerToServerAuth({
    clientId: "...",
    clientSecret: "...",
    imsBaseUrl: "https://ims-na1-stg1.adobelogin.com",
});
```

### Recupero personalizzato

Per il supporto proxy o la configurazione TLS personalizzata:

```typescript
import { ProxyAgent } from "undici";

const proxyAgent = new ProxyAgent("http://corporate-proxy:8080");

const auth = new ServerToServerAuth({
    clientId: "...",
    clientSecret: "...",
    fetch: (url, init) => fetch(url, { ...init, dispatcher: proxyAgent }),
});
```

---

## Sicurezza della contemporaneità

TypeScript SDK è sicuro per l'uso contemporaneo. Quando vengono eseguite più chiamate `getToken()` contemporaneamente e serve un aggiornamento, viene attivata una sola richiesta di aggiornamento. Le altre attendono la stessa promessa e ricevono lo stesso risultato. Non è richiesto alcun blocco esterno. Questa deduplicazione usa il ciclo di eventi a thread singolo di JavaScript e una promessa (`promise`) condivisa: se un aggiornamento è già in corso, i chiamanti contemporanei si uniscono ad esso, invece di avviare una seconda richiesta. Se `revoke()` viene chiamato mentre un aggiornamento è in corso, l'aggiornamento viene rifiutato con un `AuthenticationError` e i token rimangono cancellati: la revoca ha sempre la priorità.