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

# Guía de autenticación del SDK para Python de Frame.io

Esta guía explica cómo autenticarse con la API de Frame.io mediante el **SDK para Python de Frame.io** (`frameio`). La API V4 de Frame.io usa [Adobe Identity Management Service (IMS)](https://developer.adobe.com/developer-console/docs/guides/authentication/), la plataforma de identidad OAuth 2.0 de Adobe. Esta es una referencia independiente para desarrolladores de Python. Todos los ejemplos de código y flujos que aparecen a continuación son solo para el paquete `frameio`.

---

## Tipos de autenticación en el SDK de Python

El SDK de Python admite cuatro opciones de autenticación:

| Método                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | Caso de uso                                                                             | ¿Hay interacción del usuario? | ¿Requiere secreto de cliente? |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ----------------------------- | ----------------------------- |
| **Token estático**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Scripts rápidos, pruebas o ya tiene un token                                            | No                            | No                            |
| **Servidor a servidor**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Servicios backend, trabajos cron, automatización                                        | No                            | Sí                            |
| **Web App**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Aplicaciones del lado del servidor (Flask, Django, FastAPI)                             | Sí                            | Sí                            |
| **SPA (PKCE)**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Aplicaciones de navegador, CLI o cualquier aplicación que no pueda almacenar un secreto | Sí                            | No                            |
| **Servidor a servidor** permite que la aplicación actúe como una cuenta de servicio sin interacción del usuario. Solo está disponible para cuentas de Frame.io V4 administradas mediante [Adobe Admin Console](https://adminconsole.adobe.com/). **Web App** y **SPA** permiten que su aplicación actúe como un usuario específico. Ambos utilizan Adobe IMS de forma independiente: el usuario autoriza la aplicación y el SDK intercambia el código resultante por tokens. El SDK de Python maneja el flujo de IMS `/authorize/v2` y `/token/v3` por usted. Para Web App necesita un secreto de cliente; para SPA, utiliza [PKCE](https://datatracker.ietf.org/doc/html/rfc7636) en su lugar. |                                                                                         |                               |                               |

> **Note**
>
> La [credencial de Native App](https://developer.adobe.com/developer-console/docs/guides/authentication/UserAuthentication/implementation/#oauth-native-app-credential) de Adobe requiere controladores de esquema de URI personalizados (por ejemplo, `adobe+<hash>://…</hash>`) que intercepten las redirecciones a nivel del sistema operativo. Python no tiene una forma estándar de registrar dichos controladores, por lo que el SDK de Python no ofrece una clase `NativeAppAuth`. Para aplicaciones Python con interacción del usuario, utilice `WebAppAuth` con un servidor de devolución de llamada local (por ejemplo, Flask o FastAPI). Para cargas de trabajo no interactivas, utilice `ServerToServerAuth`.

---

## Usuarios de cuenta de servicio

Cuando se usa la autenticación de servidor a servidor, la aplicación actúa como **usuario de cuenta de servicio**, un tipo de cuenta independiente que puede realizar acciones en nombre del servicio. Estos usuarios son visibles para otros usuarios en Frame.io: cuando una cuenta de servicio realiza una acción, su nombre se muestra en la IU. Puede conceder y revocar el acceso de cuentas de servicio mediante [Adobe Admin Console](https://adminconsole.adobe.com/) y [Developer Console](https://developer.adobe.com/console). Los nombres de las cuentas de servicio se gestionan desde la IU de Frame.io. De forma predeterminada, la primera conexión S2S se denomina **Service Account User**, la segunda **Service Account User 2**, y así sucesivamente.

> **Info**
>
> Consulte [Automatización de la configuración con compatibilidad de servidor a servidor de Frame.io](https://helpx.adobe.com/enterprise/using/automate-using-frame-io.html) para obtener más información.

---

## Inicio rápido

### Requisitos previos

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

* **ID de cliente**: Obligatorio para todos los flujos Oauth. - **Secreto de cliente**: Obligatorio para los flujos de servidor a servidor y Web App. - **URI de redireccionamiento**: Obligatorio para los flujos Web App y SPA; debe estar registrado en el proyecto de Adobe

2. **Instale el SDK:**

```bash
pip install frameio
```

### Elección de un método

* **¿No interviene ningún usuario?** Use **Server-to-Server** (`ServerToServerAuth`).
* **¿Interviene un usuario y puede almacenar un secreto?** Use **Web App** (`WebAppAuth`).
* **¿Interviene un usuario, pero no puede almacenar un secreto?** Use **SPA** (`SPAAuth`).

---

## Token de acceso

Si ya dispone de un token de acceso, procedente de otro sistema OAuth o de un intercambio anterior, por ejemplo, mediante nuestro [API Explorer](/platform/api-reference/accounts/index?explorer=true), puede pasarlo directamente:

```python
from frameio import Frameio

client = Frameio(token="YOUR_ACCESS_TOKEN")
```

Este es el enfoque más sencillo, pero el token caducará en algún momento y el SDK no lo actualizará automáticamente.

### Tokens de desarrollador heredados

En el caso de las cuentas migradas a V4 que aún no se administran mediante [Adobe Admin Console](https://adminconsole.adobe.com/), puede seguir usando tokens de desarrollador heredados del [sitio para desarrolladores de Frame.io](https://developer.frame.io/app/tokens). Debe incluir el encabezado `x-frameio-legacy-token-auth` y establecerlo en `true`:

```python
from frameio import Frameio

client = Frameio(
    token="YOUR_LEGACY_DEVELOPER_TOKEN",
    headers={"x-frameio-legacy-token-auth": "true"},
)
```

Los tokens de desarrollador heredados no caducan, pero son un mecanismo de transición. Para integraciones nuevas y cargas de trabajo de producción, recomendamos usar uno de los flujos OAuth 2.0 que se indican a continuación. Consulte la [Guía de migración](/platform/docs/resources/migration#authentication) para obtener más información.

---

## Servidor a servidor (credenciales de cliente)

Use este método para servicios backend y scripts que necesiten acceso a Frame.io sin interacción del usuario. Este flujo solo está disponible para cuentas de Frame.io V4 administradas mediante [Adobe Admin Console](https://adminconsole.adobe.com/). La aplicación se autentica como [usuario de cuenta de servicio](#service-account-users) sin intervención humana.

#### Síncrono

```python
    from frameio import Frameio
    from frameio.auth import ServerToServerAuth

    auth = ServerToServerAuth(
        client_id="YOUR_CLIENT_ID",
        client_secret="YOUR_CLIENT_SECRET",
    )

    client = Frameio(token=auth.get_token)
```

#### Asíncrono

```python
    from frameio import AsyncFrameio
    from frameio.auth import AsyncServerToServerAuth

    auth = AsyncServerToServerAuth(
        client_id="YOUR_CLIENT_ID",
        client_secret="YOUR_CLIENT_SECRET",
    )

    client = AsyncFrameio(token=auth.get_token)
```

Eso es todo. `auth.getToken()` es un elemento invocable que el SDK invoca en cada solicitud. Si el token actual sigue siendo válido, se devuelve inmediatamente. Si está a punto de caducar, primero se obtiene uno nuevo de forma totalmente transparente.

### Funcionamiento

Las credenciales de cliente, es decir, el ID de cliente y el secreto, **nunca caducan**. Solo se rotan manualmente por motivos de seguridad. S2S proporciona acceso a la API prácticamente permanente e ininterrumpido, sin intervención manual.

En segundo plano:

1. En la primera llamada de API, `get_token` solicita un nuevo token de acceso a Adobe IMS mediante la concesión `client_credentials`.
2. El token se almacena en caché en memoria. Los tokens de acceso individuales caducan (normalmente en 24 horas), pero el SDK se encarga de gestionarlo.
3. Cuando un token almacenado en caché se encuentra dentro del búfer de actualización, cuyo valor predeterminado es 60 segundos antes de la caducidad, el SDK obtiene automáticamente uno nuevo con las mismas credenciales de cliente.
4. No intervienen tokens de actualización. Las propias credenciales de cliente son el secreto de larga duración y siempre se pueden usar para emitir un nuevo token de acceso.

### Autenticación explícita

Si desea obtener el token de forma anticipada (por ejemplo, para detectar rápidamente credenciales incorrectas al iniciar):

```python
auth = ServerToServerAuth(client_id="...", client_secret="...")
auth.authenticate()  # raises AuthenticationError if credentials are invalid
client = Frameio(token=auth.get_token)
```

---

## Web App (código de autorización)

Use este método para aplicaciones del lado del servidor en las que los usuarios inician sesión con su Adobe ID. Este flujo requiere un secreto de cliente, que debe almacenarse de forma segura en el servidor.

#### Redirección del usuario a Adobe IMS

#### Síncrono

```python
    auth.refresh()  # fetches a new access token using the refresh token
```

#### Asíncrono

```python
    await auth.refresh()
```

#### Gestión de la devolución de llamada

Cuando Adobe IMS redirija al usuario de vuelta a `redirect_uri`, extraiga los parámetros `code` y `state`. Verifique que state coincida con el valor almacenado y, a continuación, intercambie code por tokens:

#### Síncrono

```python
    auth.refresh()  # fetches a new access token using the refresh token
```

#### Asíncrono

```python
    await auth.refresh()
```

Esto intercambia el código de autorización por un token de acceso y un token de actualización, y almacena ambos internamente.

#### Uso del cliente

#### Síncrono

```python
        from frameio import Frameio

        client = Frameio(token=auth.get_token)
```

#### Asíncrono

```python
        from frameio import AsyncFrameio

        client = AsyncFrameio(token=auth.get_token)
```

Eso es todo. A partir de este momento, `get_token` gestiona automáticamente el ciclo de vida del token. Cuando el token de acceso se acerca a la caducidad, el SDK usa el token de actualización para obtener uno nuevo. No se requiere interacción del usuario.

### Ejemplo completo de Flask

```python
import secrets
from flask import Flask, redirect, request, session
from frameio import Frameio
from frameio.auth import WebAppAuth

app = Flask(__name__)
app.secret_key = secrets.token_bytes(32)

auth = WebAppAuth(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    redirect_uri="http://localhost:5000/callback",
)

@app.route("/login")
def login():
    state = secrets.token_urlsafe(32)
    session["oauth_state"] = state
    return redirect(auth.get_authorization_url(state=state))

@app.route("/callback")
def callback():
    if request.args.get("state") != session.pop("oauth_state", None):
        return "Invalid state parameter", 403

    auth.exchange_code(code=request.args["code"])

    client = Frameio(token=auth.get_token)
    accounts = client.accounts.index()
    return f"Authenticated — {len(accounts.data)} account(s) accessible."
```

---

## Single Page App/PKCE (código de autorización + PKCE)

Use este método para aplicaciones basadas en explorador, aplicaciones de escritorio o herramientas de CLI que no puedan almacenar de forma segura un secreto de cliente. Este flujo usa [PKCE (RFC 7636)](https://datatracker.ietf.org/doc/html/rfc7636) para proteger el intercambio del código de autorización.

#### Generación de la URL de autorización

#### Síncrono

```python
        from frameio.auth import SPAAuth

        auth = SPAAuth(
            client_id="YOUR_CLIENT_ID",
            redirect_uri="https://yourapp.com/callback",
        )

        import secrets
        state = secrets.token_urlsafe(32)

        result = auth.get_authorization_url(state=state)
        # result.url            -> redirect the user here
        # result.code_verifier  -> store this securely until the callback
```

#### Asíncrono

```python
        from frameio.auth import AsyncSPAAuth

        auth = AsyncSPAAuth(
            client_id="YOUR_CLIENT_ID",
            redirect_uri="https://yourapp.com/callback",
        )

        # get_authorization_url is synchronous (no I/O needed)
        import secrets
        state = secrets.token_urlsafe(32)

        result = auth.get_authorization_url(state=state)
```

`get_authorization_url` devuelve un `AuthorizationUrlResult` que contiene la URL completa (con `code_challenge` de PKCE insertado) y el `code_verifier` que necesitará en el paso siguiente.

#### Intercambio del código con el verificador

Cuando se redirija al usuario de vuelta:

#### Síncrono

```python
        auth.exchange_code(
            code="CODE_FROM_CALLBACK",
            code_verifier=result.code_verifier,
        )
```

#### Asíncrono

```python
        await auth.exchange_code(
            code="CODE_FROM_CALLBACK",
            code_verifier=result.code_verifier,
        )
```

#### Uso del cliente

#### Síncrono

```python
        from frameio import Frameio

        client = Frameio(token=auth.get_token)
```

#### Asíncrono

```python
        from frameio import AsyncFrameio

        client = AsyncFrameio(token=auth.get_token)
```

Eso es todo. La actualización funciona igual que en Web App: el SDK usa automáticamente el token de actualización. La diferencia es que no se envía ningún secreto de cliente durante la actualización, ya que el flujo SPA está diseñado para clientes públicos.

---

## Uso asíncrono

Todas las clases de autenticación tienen una equivalente asíncrona con el prefijo `Async`. Los ejemplos de código anteriores incluyen las pestañas **Síncrono** y **Asíncrono** cuando corresponde.

| Síncrono                                                                                                                                                                                                                  | Asíncrono                 |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| `ServerToServerAuth`                                                                                                                                                                                                      | `AsyncServerToServerAuth` |
| `WebAppAuth`                                                                                                                                                                                                              | `AsyncWebAppAuth`         |
| `SPAAuth`                                                                                                                                                                                                                 | `AsyncSPAAuth`            |
| La API es idéntica. `get_authorization_url` sigue siendo síncrono, ya que no realiza E/S, mientras que `exchange_code`, `refresh`, `revoke` y `get_token` son `asíncronos`. Use las clases asíncronas con `AsyncFrameio`. |                           |

### Actualización manual de tokens

En los flujos Web App y SPA, el SDK actualiza los tokens automáticamente mediante `get_token`. Si necesita control explícito, puede llamar directamente a `refresh()`:

#### Síncrono

```python
    auth.refresh()  # fetches a new access token using the refresh token
```

#### Asíncrono

```python
    await auth.refresh()
```

Esto resulta útil si desea forzar una actualización antes de una operación crítica en lugar de depender del búfer de actualización automático.

---

## Persistencia de tokens

Todas las clases de autenticación admiten `export_tokens()` e `import_tokens()` para conservar el estado de los tokens entre reinicios. En los flujos Web App y SPA, esto es especialmente importante, ya que los tokens de acceso y de actualización se almacenan en memoria de forma predeterminada. Si la aplicación se reinicia, los usuarios tendrían que volver a autenticarse a menos que los tokens se conserven. En Server-to-Server, la persistencia es opcional, ya que las credenciales de cliente siempre pueden emitir un token nuevo, pero importar un token almacenado en caché evita un recorrido adicional al iniciar.

### Exportación e importación

```python
# After exchange_code(), save the token state
token_data = auth.export_tokens()
# token_data is a dict: {"access_token": "...", "refresh_token": "...", "expires_at": 1234567890.0}
# Save it to your database, file, or secret store

# On next startup, restore it
auth.import_tokens(token_data)
client = Frameio(token=auth.get_token)
# The SDK will automatically refresh if the token is near expiry
```

### Persistencia automática con `on_token_refreshed`

Para conservar los tokens automáticamente cada vez que se actualicen, use la devolución de llamada `on_token_refreshed`:

```python
import json
from pathlib import Path

TOKEN_FILE = Path("tokens.json")

def save_tokens(tokens: dict):
    TOKEN_FILE.write_text(json.dumps(tokens))

auth = WebAppAuth(
    client_id="...",
    client_secret="...",
    redirect_uri="...",
    on_token_refreshed=save_tokens,
)

# On startup, restore if available
if TOKEN_FILE.exists():
    auth.import_tokens(json.loads(TOKEN_FILE.read_text()))
```

La devolución de llamada recibe la misma estructura de diccionario que `export_tokens()` y se activa después de cada actualización correcta del token. En las clases asíncronas, `on_token_refreshed` puede ser una función normal o una función `asíncrona`. Ambas son compatibles.

---

## Revocación de tokens

Para cerrar la sesión de un usuario e invalidar sus tokens con Adobe IMS:

```python
auth.revoke()
```

Esto realiza una solicitud de revocación con el máximo esfuerzo a Adobe IMS tanto para el token de acceso como para el token de actualización y, a continuación, borra todo el estado local de los tokens. Después de la revocación, el usuario tendrá que volver a autenticarse.

> **Tip**
>
> En las clases asíncronas, use `await auth.revoke()`.

---

## Gestión de errores

Todos los errores de autenticación heredan de `FrameioAuthError`, por lo que puede capturarlos de forma general o gestionar casos específicos:

```python
from frameio.auth import (
    FrameioAuthError,
    AuthenticationError,
    TokenExpiredError,
    ConfigurationError,
    NetworkError,
    RateLimitError,
)

try:
    auth.exchange_code(code="...")
except TokenExpiredError:
    # The refresh token has expired; redirect the user to sign in again
    pass
except AuthenticationError as e:
    # Token exchange failed
    print(f"Error: {e.error_code} - {e.error_description}")
except NetworkError:
    # Timeout or connection failure (after retries)
    pass
except RateLimitError as e:
    # 429 from Adobe IMS; retry after e.retry_after seconds
    pass
except FrameioAuthError:
    # Catch-all for any other auth error
    pass
```

### Referencia de errores

| Excepción             | Cuándo se genera                                                                                                |
| --------------------- | --------------------------------------------------------------------------------------------------------------- |
| `ConfigurationError`  | Configuración ausente o no válida (por ejemplo, `client_id` vacío o URI de redireccionamiento que no usa HTTPS) |
| `AuthenticationError` | Adobe IMS rechaza el intercambio o la actualización del token (tiene `.error_code` y `.error_description`)      |
| `TokenExpiredError`   | El propio token de actualización ha caducado; el usuario debe volver a autenticarse                             |
| `NetworkError`        | Tiempo de espera HTTP agotado o error de conexión tras todos los reintentos                                     |
| `RateLimitError`      | Adobe IMS ha devuelto 429; consulte `.retry_after` para orientarse sobre la espera                              |
| `PKCEError`           | Error en la verificación de PKCE (flujo SPA)                                                                    |

### Gestión de tokens de actualización caducados en producción

> **Warning**
>
> En los flujos Web App y SPA, el token de actualización acabará caducando. Cuando esto ocurra, `get_token` generará `TokenExpiredError`. Debe capturarlo y redirigir al usuario de nuevo a través del flujo de autorización.

```python
from frameio.auth import TokenExpiredError

try:
    client = Frameio(token=auth.get_token)
    assets = client.files.list(project_id="...")
except TokenExpiredError:
    # Clear persisted tokens and redirect user to login
    auth.revoke()
    return redirect("/login")
```

---

## Referencia de configuración

Todas las clases de autenticación aceptan estos parámetros opcionales:

#### Referencia de parámetros

| Parámetro            | Predeterminado                                | Descripción                                                                                                                                                                                                                      |
| -------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scopes`             | Valores predeterminados específicos del flujo | Ámbitos OAuth separados por espacios. S2S usa de forma predeterminada `openid AdobeID frame.s2s.all`; los flujos orientados al usuario usan de forma predeterminada `openid email profile offline_access additional_info.roles`. |
| `ims_base_url`       | `https://ims-na1.adobelogin.com`              | URL base de Adobe IMS. Sobrescríbala para entornos de ensayo o no productivos.                                                                                                                                                   |
| `http_client`        | `None`                                        | `httpx personalizado.Cliente` (o `httpx.AsyncClient`) para proxy, mTLS o agrupación de conexiones.                                                                                                                               |
| `timeout`            | `30`                                          | Tiempo de espera de peticiones HTTP en segundos para llamadas al punto final de token.                                                                                                                                           |
| `max_retries`        | `2`                                           | Número máximo de reintentos para errores transitorios (como 5xx o tiempos de espera) agotados. Los reintentos por límite de frecuencia (429) se registran por separado.                                                          |
| `refresh_buffer`     | `60`                                          | Segundos antes de la caducidad del token para activar una actualización proactiva.                                                                                                                                               |
| `on_token_refreshed` | `None`                                        | Devolución de llamada que se activa después de cada actualización correcta del token. Recibe un diccionario con `access_token`, `refresh_token` y `expires_at`.                                                                  |

### Entornos de ensayo

Apunte a una instancia de ensayo de Adobe IMS sobrescribiendo `ims_base_url`. El SDK también exporta `DEFAULT_IMS_BASE_URL` (`https://ims-na1.adobelogin.com`) por si necesita hacer referencia al valor de producción mediante programación.

```python
auth = ServerToServerAuth(
    client_id="...",
    client_secret="...",
    ims_base_url="https://ims-na1-stg1.adobelogin.com",
)
```

### Cliente HTTP personalizado

Para compatibilidad con proxy o configuración de TLS personalizada:

```python
import httpx

http_client = httpx.Client(
    proxy="http://corporate-proxy:8080",
    verify="/path/to/custom-ca-bundle.pem",
)

auth = ServerToServerAuth(
    client_id="...",
    client_secret="...",
    http_client=http_client,
)
```

---

## Seguridad de subprocesos

Las clases de autenticación síncronas son totalmente seguras para subprocesos. Cuando varios subprocesos llaman a `get_token` simultáneamente y se necesita una actualización, solo uno de ellos realiza la actualización. Los demás esperan y reciben el mismo resultado. No se requiere ningún bloqueo externo. Las clases asíncronas ofrecen la misma garantía mediante `asyncio.Lock`, lo que resulta seguro para corrutinas concurrentes dentro de un único bucle de eventos.