> This page is for Plataforma, version V4 (default).
> For other versions, use one of these documentation indexes:
> - V4 (default): https://next.developer.frame.io/platform/v4/llms.txt
> - V4 experimental: https://next.developer.frame.io/platform/v4-experimental/llms.txt
> - Herdado: 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.

# Frame.io TypeScript SDK - Guia de autenticação

Este guia explica como autenticar com a API do Frame.io usando o **SDK TypeScript do Frame.io** (`frameio`). A API Frame.io V4 usa o [Adobe Identity Management Service (IMS)](https://developer.adobe.com/developer-console/docs/guides/authentication/), a plataforma de identidade OAuth 2.0 da Adobe. Esta é uma referência independente para desenvolvedores TypeScript/JavaScript. Todos os exemplos de código e fluxos abaixo são apenas para o pacote `frameio`

---

## Tipos de autenticação no SDK TypeScript

O SDK TypeScript oferece suporte a quatro classes de autenticação OAuth, além do uso direto de token:

| Método                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Caso de uso                                                                      | Interação do usuário? | Requer client secret? |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------------- | --------------------- |
| **Token estático**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | Scripts rápidos, testes ou quando você já tem um token                           | Não                   | Não                   |
| **Servidor para servidor**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Serviços de backend, cron jobs, automação                                        | Não                   | Sim                   |
| **Aplicativo web**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | Aplicativos do lado do servidor (Express, Fastify, Next.js)                      | Sim                   | Sim                   |
| **SPA (PKCE)**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | Aplicativos de navegador que não podem armazenar um segredo                      | Sim                   | Não                   |
| **Aplicativo nativo (PKCE)**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | Aplicativos desktop/móveis com redirecionamentos de esquema de URI personalizado | Sim                   | Não                   |
| **Server-to-Server** permite que o aplicativo atue como uma conta de serviço sem interação do usuário.Está disponível somente para contas do Frame.io V4 administradas por meio do [Adobe Admin Console](https://adminconsole.adobe.com/).**Web App** e **SPA** permitem que o aplicativo atue como um usuário específico.Ambos usam Adobe IMS por baixo: o usuário autoriza o aplicativo, e o SDK troca o código resultante por tokens.O SDK do TypeScript processa o fluxo `/authorize/v2` e `/token/v3` do IMS para você. Para Web App você precisa de um client secret; para SPA você usa [PKCE](https://datatracker.ietf.org/doc/html/rfc7636).**Aplicativo nativo** segue o mesmo fluxo PKCE do SPA, mas usa o URI de redirecionamento `adobe+<hash>://callback</hash>` URI de redirecionamento que a Adobe atribui à sua credencial de aplicativo nativo.Isso permite que o aplicativo intercepte o redirecionamento no nível do SO após a autorização. |                                                                                  |                       |                       |

---

## Usuários de conta de serviço

Ao usar autenticação Server-to-Server, seu aplicativo atua como um **usuário de conta de serviço**, um tipo distinto de conta que pode executar ações em nome do serviço. Eles ficam visíveis para outros usuários no Frame.io: quando uma conta de serviço executa uma ação, seu nome é exibido na IU. Você pode conceder e revogar o acesso da conta de serviço pelo [Adobe Admin Console](https://adminconsole.adobe.com/) e [Developer Console](https://developer.adobe.com/console). Os nomes de conta de serviço são gerenciados pela IU do Frame.io. Por padrão, a primeira conexão S2S é nomeada **Usuários de conta de serviço**, a segunda **Usuários de conta de serviço 2** e assim por diante.

> **Info**
>
> Consulte [Automatizar a configuração usando suporte servidor para servidor Frame.io](https://helpx.adobe.com/enterprise/using/automate-using-frame-io.html) para obter mais informações.

---

## Início rápido

### Pré-requisitos

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

* **ID do cliente** — obrigatório para todos os fluxos OAuth - **Segredo do cliente** — obrigatório para fluxos servidor a servidor e aplicativo web - **URI de redirecionamento** — obrigatório para fluxos Web App, SPA e Native App; deve ser registrado no seu projeto da Adobe

2. **Instalar o SDK:**

```bash
npm install frameio
```

### Escolha de um método

* \*\*Nenhum usuário envolvido?\*\*Use **Server-to-Server** (`ServerToServerAuth`).
* \*\*Usuário envolvido e você pode armazenar um segredo?\*\*Use **Web App** (`WebAppAuth`).
* \*\*Usuário envolvido, mas você não pode armazenar um segredo?\*\*Use **SPA** (`SPAAuth`) para aplicativos de navegador ou **Native App** (`NativeAppAuth`) para aplicativos desktop/para dispositivos móveis.

---

## Token de acesso

Se você já tiver um token de acesso de outro sistema OAuth ou de uma troca anterior, por exemplo, pelo nosso [API Explorer](/platform/api-reference/accounts/index?explorer=true), poderá passá-lo diretamente:

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

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

Essa é a abordagem mais simples, mas o token acabará expirando e o SDK não o atualizará para você.

### Tokens de desenvolvedor legados

Para contas migradas para a V4 que ainda não são administradas pelo [Adobe Admin Console](https://adminconsole.adobe.com/), você pode continuar usando Tokens de desenvolvedor legados do [site de desenvolvedores do Frame.io](https://developer.frame.io/app/tokens). Você deve incluir o cabeçalho `x-frameio-legacy-token-auth` e defini-lo como `true`:

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

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

Tokens de desenvolvedor legados não expiram, mas são um mecanismo de transição. Para novas integrações e cargas de trabalho de produção, recomendamos usar um dos fluxos OAuth 2.0 abaixo. Consulte o [Guia de migração](/platform/docs/resources/migration#authentication) para obter detalhes.

---

## Servidor para servidor (Credenciais do cliente)

Use isso para serviços de backend e scripts que precisam de acesso ao Frame.io sem interação do usuário. Esse fluxo só está disponível para contas Frame.io V4 administradas pelo [Adobe Admin Console](https://adminconsole.adobe.com/). Seu aplicativo se autentica como um [usuário de conta de serviço](#service-account-users) sem intervenção humana.

```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() });
```

É isso. `auth.getToken()` é uma função assíncrona que o SDK invoca em cada solicitação. Se o token atual ainda for válido, ele retornará imediatamente. Se estiver prestes a expirar, ele buscará um novo primeiro, de forma totalmente transparente.

### Como funciona

Suas credenciais do cliente (ID + segredo do cliente) **nunca expiram**. Você só precisa alterná-las manualmente por higiene de segurança. S2S oferece acesso à API efetivamente permanente e ininterrupto, sem nenhuma intervenção manual.

Nos bastidores:

1. Na primeira chamada da API, `getToken()` um novo token de acesso do Adobe IMS usando a concessão `client_credentials`.
2. O token é armazenado em cache na memória. Tokens de acesso individuais expiram (normalmente em 24 horas), mas isso é tratado para você.
3. Quando um token em cache está dentro do buffer de atualização (padrão: 60 segundos antes da expiração), o SDK busca um novo automaticamente usando as mesmas credenciais de cliente.
4. Não há tokens de atualização envolvidos. As próprias credenciais de cliente são o segredo de longa duração e sempre podem ser usadas para emitir um novo token de acesso.

### Autenticação explícita

Se quiser buscar o token antecipadamente, por exemplo, para falhar rapidamente com credenciais incorretas na inicialização:

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

---

## Aplicativo web (Código de autorização)

Use isso para aplicativos do lado do servidor em que os usuários fazem logon com sua Adobe ID. Este fluxo requer um segredo do cliente, que deve ser armazenado com segurança no servidor.

#### Redirecionar o usuário para o 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`
```

#### Processar o callback

Quando o Adobe IMS redireciona o usuário de volta para o `redirectUri`, extraia os parâmetros `code` e `state`. Verifique se o estado corresponde ao que você armazenou e troque o código por tokens:

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

Isso troca o código de autorização por um token de acesso e um token de atualização, armazenando ambos internamente.

#### Use o cliente

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

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

É isso. A partir deste ponto, `getToken()` o ciclo de vida do token automaticamente. Quando o token de acesso se aproxima do vencimento, o SDK usa o token de atualização para obter um novo. Nenhuma interação do usuário necessária.

### Exemplo completo em 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);
```

---

## Aplicativo de página única / PKCE (Código de autorização + PKCE)

Use isso para aplicativos baseados em navegador, aplicativos para desktop ou ferramentas de CLI que não conseguem armazenar um client secret com segurança. Este fluxo usa [PKCE (RFC 7636)](https://datatracker.ietf.org/doc/html/rfc7636) para proteger a troca de código de autorização.

#### Gerar a URL de autorização

```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` retorna um `AuthorizationUrlResult` contendo a URL completa (com o PKCE `code_challenge` integrado) e o `codeVerifier` que você precisa na próxima etapa.

#### Trocar o código pelo verificador

Quando o usuário for redirecionado de volta:

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

#### Use o cliente

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

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

É isso. A atualização funciona da mesma forma que no Web App - o SDK usa o token de atualização automaticamente. A diferença é que nenhum client secret é enviado durante a atualização, pois o fluxo SPA foi projetado para clientes públicos.

> **Warning**
>
> O `codeVerifier` deve ser armazenado com segurança no lado do cliente entre a solicitação de autorização e a troca do código. Use `sessionStorage` ou equivalente em aplicativos de navegador.

---

## Native App (código de autorização + PKCE)

Use isso para aplicativos de desktop e mobile. Quando você cria uma credencial de Native App no [Adobe Developer Console](https://developer.adobe.com/console), a Adobe atribui um URI de redirecionamento do formulário `adobe+<hash>://callback</hash>` — você registra seu aplicativo para processar esse esquema de URI personalizado no nível do sistema operacional. Redirecionamentos de loopback (`http://127.0.0.1:<port>/callback</port>`) também são compatíveis com desenvolvimento local. O fluxo é idêntico ao SPA: usa PKCE sem client secret.

```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() });
```

### Regras de URI de redirecionamento

A Adobe aplica regras de URI de redirecionamento em dois pontos: quando você registra a credencial no [Adobe Developer Console](https://developer.adobe.com/console) e quando o parâmetro `redirect_uri` chega ao ponto de acesso `/authorize/v2`. O valor que você passa para `redirectUri` neste SDK deve corresponder a um dos "Padrões de URI de redirecionamento" registrados na credencial; caso contrário, a Adobe redireciona para a URI de redirecionamento padrão na credencial.

* Credenciais de **Web App** e **SPA** exigem HTTPS.
* Credenciais **Native App** usam um redirecionamento não HTTPS, normalmente a URI `adobe+<hash>://callback</hash>` mostrado no Developer Console para a credencial.

Consulte o [Adobe Developer Console](https://developer.adobe.com/console) para os padrões exatos aceitos para sua credencial.

> **Note**
>
> O SDK para Python não inclui uma classe de credencial Native App, pois o Python não tem uma forma padrão de registrar manipuladores
>
> de esquema de URI personalizado. O SDK para TypeScript oferece suporte a todos os quatro tipos de credenciais, incluindo Native App.

---

## Atualização manual de token

Para fluxos Web App, SPA e Native App, o SDK atualiza tokens automaticamente por meio de `getToken()`. Se você precisar de controle explícito, pode chamar `refresh()` diretamente:

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

Isso é útil quando você quer forçar uma atualização antes de uma operação crítica, em vez de depender do buffer de atualização automática.

`refresh()` está disponível no `WebAppAuth`, `SPAAuth` e `NativeAppAuth`. Ele gera `ConfigurationError` se nenhum token de atualização estiver disponível (ou seja, você deve chamar `exchangeCode()` primeiro). `ServerToServerAuth` não tem um método `refresh()` — ele usa `authenticate()` para buscar um novo token via credenciais do cliente.

---

## Persistência de tokens

Todas as classes de autenticação oferecem suporte a `exportTokens()` e `importTokens()` para persistir o estado do token entre reinicializações. Para fluxos Web App, SPA e Native App, isso é especialmente importante, pois os tokens de acesso e de atualização ficam na memória por padrão. Se o aplicativo reiniciar, os usuários precisarão se autenticar novamente, a menos que você persista esses tokens. Para servidor para servidor, a persistência é opcional (as credenciais de cliente sempre podem emitir um novo token), mas importar um token em cache evita uma ida e volta extra na inicialização.

### Exportar e importar

```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**
>
> Armazene os tokens exportados com segurança. Eles contêm tokens de acesso e de atualização que concedem acesso à API. Evite escrever tokens
>
> em arquivos de texto sem formatação na produção.

### Persistência automática com `onTokenRefreshed`

Para persistir tokens automaticamente sempre que forem atualizados, use o 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
}
```

O callback recebe o mesmo formato de `exportTokens()` e é acionado após cada atualização de token bem-sucedida.

---

## Revogação de tokens

Para encerrar a sessão de um usuário e invalidar seus tokens com o Adobe IMS:

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

Isso faz duas solicitações de revogação de melhor esforço ao Adobe IMS, uma para o token de acesso e outra para o token de atualização, em paralelo, e limpa todo o estado local do token.Para clientes confidenciais (`WebAppAuth`), as solicitações de revogação usam autenticação básica HTTP; para clientes públicos (`SPAAuth`, `NativeAppAuth`), o `client_id` é enviado como parâmetro de consulta. Erros de revogação são registrados, mas não gerados. Após a revogação, o usuário precisará se autenticar novamente.

---

## Tratamento de erros

Todos os erros de autenticação herdam de `FrameioAuthError`, portanto você pode capturá-los de forma ampla ou tratar casos específicos:

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

### Referência de erros

| Exceção               | Quando é gerada                                                                                                             |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `ConfigurationError`  | `Configuração ausente ou inválida (por exemplo, clientId` vazio, URI de redirecionamento não HTTPS, `imsBaseUrl` não HTTPS) |
| `AuthenticationError` | `Troca ou atualização de token rejeitada pelo Adobe IMS (tem .errorCode` e `.errorDescription`)                             |
| `TokenExpiredError`   | O próprio token de atualização expirou; o usuário deve se autenticar novamente                                              |
| `NetworkError`        | Tempo-limite HTTP ou falha de conexão após todas as novas tentativas                                                        |
| `RateLimitError`      | O Adobe IMS retornou 429; verifique `.retryAfter` para orientação de backoff                                                |
| `PKCEError`           | Disponível para uso do consumidor em fluxos PKCE; não é gerado internamente pelo SDK                                        |

### Tratamento de tokens de atualização expirados em produção

> **Warning**
>
> Para fluxos Web App, SPA e Native App, o token de atualização acabará expirando.Quando isso acontecer, `getToken()` gera `TokenExpiredError`. Você deve capturar isso e redirecionar o usuário pelo fluxo de autorização novamente.

```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");
    }
}
```

---

## Referência de configuração

Esses parâmetros têm padrões sensatos e raramente precisam ser definidos. Se precisar personalizar o comportamento, apontar para um IMS de staging, injetar um `fetch` personalizado, ajustar tempos-limite ou conectar um logger, passe qualquer um deles como parâmetros opcionais ao construir a classe de autenticação:

| Parâmetro          | Padrão                           | Descrição                                                                                                                                                                                       |
| ------------------ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scopes`           | Padrões específicos do fluxo     | Escopos OAuth separados por espaços. S2S usa por padrão `openid AdobeID frame.s2s.all`; fluxos voltados ao usuário têm como padrão `openid email profile offline_access additional_info.roles`. |
| `imsBaseUrl`       | `https://ims-na1.adobelogin.com` | URL base do Adobe IMS. Substitua para ambientes de preparo ou não produção. Deve usar HTTPS.                                                                                                    |
| `fetch`            | `globalThis.fetch`               | Implementação personalizada de `fetch` para proxy, mTLS ou manipulação HTTP personalizada.                                                                                                      |
| `timeout`          | `30000`                          | Tempo-limite de solicitação HTTP em milissegundos para chamadas ao ponto de acesso de token.                                                                                                    |
| `maxRetries`       | `2`                              | Número máximo de novas tentativas para falhas transitórias (5xx, tempos-limite). Novas tentativas por limite de taxa (429) são rastreadas separadamente.                                        |
| `refreshBuffer`    | `60`                             | Segundos antes da expiração do token para acionar a atualização proativa.                                                                                                                       |
| `onTokenRefreshed` | `undefined`                      | Callback disparado após cada atualização de token bem-sucedida.Recebe um objeto com `access_token`, `refresh_token` e `expires_at`.                                                             |
| `logger`           | Sem operação (silencioso)        | Instância do logger com métodos `debug`, `info`, `warn`, `error` (por exemplo `console`, `pino`, `winston`).                                                                                    |

### Ambientes de preparo

Aponte para uma instância de staging do Adobe IMS substituindo `imsBaseUrl`. O SDK também exporta `DEFAULT_IMS_BASE_URL` (`https://ims-na1.adobelogin.com`) se precisar referenciar o valor de produção programaticamente.

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

### Busca personalizada

Para suporte a proxy ou configuração TLS personalizada:

```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 }),
});
```

---

## Segurança de concorrência

O SDK para TypeScript é seguro para uso simultâneo. Quando várias chamadas `getToken()` acontecem simultaneamente e uma atualização é necessária, apenas uma solicitação de atualização é disparada. As outras aguardam a mesma promise e recebem o mesmo resultado. Nenhum bloqueio externo é necessário. Essa deduplicação usa o loop de eventos de thread única do JavaScript e uma `Promise` compartilhada. Se uma atualização já estiver em andamento, chamadores simultâneos se juntam a ela em vez de iniciar uma segunda solicitação. Se `revoke()` for chamado enquanto uma atualização estiver em andamento, a atualização será rejeitada com `AuthenticationError` e os tokens permanecerão limpos. A revogação sempre vence.