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

# Guide d’authentification du SDK Python de Frame.io

Ce guide explique comment s’authentifier auprès de l’API Frame.io à l’aide du **SDK Python Frame.io** (`frameio`). L’API Frame.io V4 utilise le service [Adobe Identity Management (IMS)](https://developer.adobe.com/developer-console/docs/guides/authentication/), la plateforme d’authentification OAuth 2.0 d’Adobe. Il s’agit d’un guide de référence autonome destiné aux développeurs Python. Tous les exemples de code et les flux ci-dessous concernent exclusivement le package `frameio`.

---

## Types d’authentification dans le SDK Python

Le SDK Python prend en charge quatre options d’authentification :

| Méthode                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Cas d’usage                                                                                                    | Interaction utilisateur ? | Nécessite un secret client ? |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------- | ---------------------------- |
| **Jeton statique**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | Scripts rapides, test ou vous avez déjà un jeton                                                               | Non                       | Non                          |
| **De serveur à serveur**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | Services backend, tâches cron, automatisation                                                                  | Non                       | Oui                          |
| **Application web**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Applications côté serveur (Flask, Django, FastAPI)                                                             | Oui                       | Oui                          |
| **SPA (PKCE)**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Applications web, interfaces de ligne de commande (CLI) ou toute application qui ne peut pas stocker un secret | Oui                       | Non                          |
| **Serveur à serveur** permet à votre application d’agir comme un compte de service sans interaction utilisateur. Disponible uniquement pour les comptes Frame.io V4 gérés via l’[Adobe Admin Console](https://adminconsole.adobe.com/). **Appli web** et **SPA** permettent à votre application d’agir comme un utilisateur spécifique. Les deux utilisent Adobe IMS en arrière-plan : l’utilisateur autorise votre application et le SDK échange le code résultant contre des jetons. Le SDK Python gère le flux IMS `/authorize/v2` et `/token/v3` pour vous. Pour une application web, vous avez besoin d’un secret client ; pour une SPA, utilisez [PKCE](https://datatracker.ietf.org/doc/html/rfc7636) à la place. |                                                                                                                |                           |                              |

> **Note**
>
> L’option [Informations d’identification pour une application native](https://developer.adobe.com/developer-console/docs/guides/authentication/UserAuthentication/implementation/#oauth-native-app-credential) d’Adobe nécessite des gestionnaires de schéma URI personnalisés (p. ex. `adobe+<hash>://…</hash>`) qui interceptent les redirections au niveau du système d’exploitation. Python n’a pas de moyen standard pour enregistrer de tels gestionnaires, donc le SDK Python n’offre pas de classe `NativeAppAuth`. Pour les applications Python qui sollicitent une interaction de l’utilisateur, utilisez `WebAppAuth` avec un serveur de rappel local (p. ex. Flask ou FastAPI). Pour les charges de travail sans interaction, utilisez `ServerToServerAuth`.

---

## Utilisateurs de comptes de service

Lorsque vous utilisez l’authentification de serveur à serveur, votre application agit en tant qu’**utilisateur de compte de service**, un type de compte distinct qui peut effectuer des actions au nom du service. Ces informations sont visibles par les autres utilisateurs de Frame.io : lorsqu’un compte de service effectue une action, son nom s’affiche dans l’interface utilisateur. Vous pouvez accorder ou révoquer l’accès à un compte de service via l’[Adobe Admin Console](https://adminconsole.adobe.com/) et la [Developer Console](https://developer.adobe.com/console). Les noms des comptes de service sont gérés depuis l’interface utilisateur de Frame.io. Par défaut, votre première connexion S2S est nommée **Service Account User**, la deuxième \*\* Service Account User 2\*\*, et ainsi de suite.

> **Info**
>
> Voir [Automatiser votre configuration grâce à la prise en charge de serveur à serveur de Frame.io](https://helpx.adobe.com/enterprise/using/automate-using-frame-io.html) pour en savoir plus.

---

## Démarrage rapide

### Conditions préalables

1. **Informations d’identification** de l’[Adobe Developer Console](https://developer.adobe.com/console) :

* **ID client** (obligatoire pour tous les flux OAuth), **Secret client** (obligatoire pour les flux serveur à serveur et les flux d’application web), **URI de redirection** (obligatoire pour les flux d’application web et les flux SPA). Ces informations doivent être enregistrées dans votre projet Adobe.

2. **Installer le SDK :**

```bash
pip install frameio
```

### Choisir une méthode

* **Aucun utilisateur n’est concerné ?** Utilisez l’authentification **serveur à serveur** (`ServerToServerAuth`).
* **L’utilisateur est concerné et vous pouvez enregistrer un secret ?** Utilisez l’authentification par **application web** (`WebAppAuth`).
* **L’utilisateur est concerné et vous ne pouvez pas enregistrer un secret ?** Utilisez l’authentification **SPA** (`SPAAuth`).

---

## Jeton d’accès

Si vous disposez déjà d’un jeton d’accès (provenant d’un autre système OAuth ou d’un échange précédent, par exemple via notre [Explorateur des API](/platform/api-reference/accounts/index?explorer=true)), vous pouvez le transmettre directement :

```python
from frameio import Frameio

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

C’est la méthode la plus simple, mais le jeton finira par expirer et le SDK ne le renouvellera pas pour vous.

### Jetons de développeur hérités

Pour les comptes ayant migré vers la version 4 et qui ne sont pas encore gérés via l’[Adobe Admin Console](https://adminconsole.adobe.com/), vous pouvez continuer à utiliser les jetons de développeur hérités disponibles sur le [site des développeurs Frame.io](https://developer.frame.io/app/tokens). Vous devez inclure l’en-tête `x-frameio-legacy-token-auth` et le configurer sur `true` :

```python
from frameio import Frameio

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

Les jetons de développeur hérités n’expirent pas, mais ils constituent un mécanisme transitoire. Pour les nouvelles intégrations et les charges de travail en production, nous vous recommandons d’utiliser l’un des flux OAuth 2.0 ci-dessous. Pour plus de détails, consultez le [guide de migration](/platform/docs/resources/migration#authentication).

---

## Authentification de serveur à serveur (Informations d’identification)

Utilisez cette option pour les services et scripts backend qui nécessitent un accès à Frame.io sans intervention de l’utilisateur. Ce flux n’est disponible que pour les comptes Frame.io V4 gérés via l’[Adobe Admin Console](https://adminconsole.adobe.com/). Votre application s’authentifie en tant qu’[utilisateur de compte de service](#service-account-users) sans intervention humaine.

#### Sync

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

#### Async

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

Et voilà. `auth.get_token` est une fonction que le SDK appelle à chaque requête. Si le jeton actuel est toujours valide, il est renvoyé immédiatement. S’il est sur le point d’expirer, le système en récupère d’abord un nouveau, de manière totalement transparente.

### Fonctionnement

Vos informations d’identification client (ID client + secret) **n’expirent jamais**. Vous ne les changez manuellement que pour des raisons de sécurité. L’authentification S2S vous offre un accès à l’API pratiquement permanent et ininterrompu, sans aucune intervention manuelle.

En coulisses :

1. Lors du premier appel d’API, `get_token` présente une requête pour obtenir un nouveau jeton d’accès à Adobe IMS en utilisant le mode d’autorisation `client_credentials`.
2. Le jeton est mis en cache en mémoire. Les jetons d’accès individuels expirent (généralement au bout de 24 heures), mais cette gestion est prise en charge pour vous.
3. Lorsqu’un jeton mis en cache se trouve dans la mémoire tampon d’actualisation (par défaut : 60 secondes avant son expiration), le SDK en récupère automatiquement un nouveau en utilisant les mêmes informations d’identification.
4. Aucun jeton d’actualisation n’est utilisé. Les informations d’identification du client constituent en elles-mêmes un secret à long terme et peuvent toujours être utilisées pour générer un nouveau jeton d’accès.

### Authentification explicite

Si vous souhaitez récupérer le jeton dès le début (par exemple, pour détecter rapidement les informations d’identification incorrectes au démarrage) :

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

---

## Application web (code d’autorisation)

Utilisez cette option pour les applications côté serveur dans lesquelles les utilisateurs se connectent à l’aide de leur Adobe ID. Ce flux nécessite un secret client, qui doit être stocké en toute sécurité sur votre serveur.

#### Rediriger l’utilisateur vers Adobe IMS

#### Sync

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

#### Async

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

#### Gérer le rappel

Lorsqu’Adobe IMS redirige l’utilisateur vers votre `redirectUri`, extrayez les paramètres `code` et `state`. Vérifiez que l’état correspond bien à ce que vous avez enregistré, puis échangez le code contre des jetons :

#### Sync

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

#### Async

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

Cette opération permet d’échanger le code d’autorisation contre un jeton d’accès et un jeton d’actualisation, qui sont tous deux enregistrés en interne.

#### Utiliser le client

#### Sync

```python
        from frameio import Frameio

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

#### Async

```python
        from frameio import AsyncFrameio

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

Et voilà. À partir de ce point, `get_token` gère automatiquement le cycle de vie du jeton. Lorsque le jeton d’accès arrive à expiration, le SDK utilise le jeton d’actualisation pour en obtenir un nouveau. Aucune intervention de l’utilisateur n’est nécessaire.

### Exemple Flask complet

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

---

## Application monopage / PKCE (code d’autorisation + PKCE)

Utilisez cette option pour les applications web, les applications de bureau ou les outils en ligne de commande qui ne peuvent pas stocker un secret client en toute sécurité. Ce flux utilise le protocole [PKCE (RFC 7636)](https://datatracker.ietf.org/doc/html/rfc7636) pour sécuriser l’échange de codes d’autorisation.

#### Générer l’URL d’autorisation

#### Sync

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

#### Async

```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` renvoie un `AuthorizationUrlResult` contenant l’URL complète (avec le PKCE `code_challenge` intégré) et le `code_verifier` dont vous aurez besoin à l’étape suivante.

#### Échanger le code avec le vérificateur

Lorsque l’utilisateur est redirigé de retour :

#### Sync

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

#### Async

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

#### Utiliser le client

#### Sync

```python
        from frameio import Frameio

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

#### Async

```python
        from frameio import AsyncFrameio

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

Et voilà. La mise à jour fonctionne de la même manière que pour l’application web : le SDK utilise automatiquement le jeton de mise à jour. La différence réside dans le fait qu’aucun secret client n’est transmis lors de l’actualisation, car le flux SPA est conçu pour les clients publics.

---

## Utilisation d’Async

Chaque classe d’authentification possède une version asynchrone dont le nom porte le préfixe `Async`. Les exemples de code ci-dessus comportent des onglets **Sync** et **Async** lorsque cela s’y prête.

| Sync                                                                                                                                                                                                              | Async                     |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| `ServerToServerAuth`                                                                                                                                                                                              | `AsyncServerToServerAuth` |
| `WebAppAuth`                                                                                                                                                                                                      | `AsyncWebAppAuth`         |
| `SPAAuth`                                                                                                                                                                                                         | `AsyncSPAAuth`            |
| L’API est identique. `get_authorization_url` reste synchrone (pas d’E/S), tandis que `exchange_code`, `refresh`, `revoke` et `get_token` sont tous `async`. Utilisez les classes asynchrones avec `AsyncFrameio`. |                           |

### Actualisation manuelle des jetons

Pour les flux des applications web et des SPA, le SDK actualise automatiquement les jetons via `get_token`. Si vous avez besoin d’un contrôle explicite, vous pouvez appeler `refresh()` directement :

#### Sync

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

#### Async

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

Cela s’avère utile lorsque vous souhaitez forcer une actualisation avant une opération critique, plutôt que de compter sur la mémoire tampon d’actualisation automatique.

---

## Persistance des jetons

Toutes les classes d’authentification prennent en charge les méthodes `export_tokens()` et `import_tokens()` pour conserver l’état des jetons entre les redémarrages. Cela revêt une importance particulière pour les flux des applications web et des SPA, car les jetons d’accès et d’actualisation sont conservés en mémoire par défaut : si votre application redémarre, les utilisateurs devront se réauthentifier, à moins que vous ne les conserviez en mémoire. Pour les communications de serveur à serveur, la persistance est facultative (les informations d’identification du client permettent toujours de générer un nouveau jeton), mais l’importation d’un jeton mis en cache évite un aller-retour supplémentaire au démarrage.

### Exporter et importer

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

### Persistance automatique avec `on_token_refreshed`

Pour conserver automatiquement les jetons chaque fois qu’ils sont actualisés, utilisez le rappel `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()))
```

Le rappel reçoit un dictionnaire de même structure que `export_tokens()` et se déclenche après chaque actualisation réussie des jetons. Pour les classes asynchrones, `on_token_refreshed` peut être soit une fonction classique, soit une fonction `async`. Les deux sont prises en charge.

---

## Révocation des jetons

Pour déconnecter un utilisateur et invalider ses jetons avec Adobe IMS :

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

Cette opération envoie une requête de révocation (au mieux) à Adobe IMS pour le jeton d’accès et le jeton d’actualisation, puis efface l’état de tous les jetons locaux. Une fois l’accès révoqué, l’utilisateur devra s’authentifier à nouveau.

> **Tip**
>
> Pour les classes asynchrones, utilisez `await auth.revoke()`.

---

## Gestion des erreurs

Toutes les erreurs d’authentification sont dérivées de `FrameioAuthError`. Vous pouvez donc les traiter de manière globale ou gérer des cas particuliers :

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

### Références des erreurs

| Exception             | Déclenchée dans les cas suivants                                                                             |
| --------------------- | ------------------------------------------------------------------------------------------------------------ |
| `ConfigurationError`  | Configuration manquante ou non valide (p. ex. `client_id` vide, URI de redirection non HTTPS)                |
| `AuthenticationError` | Refus de l’échange ou de l’actualisation du jeton par Adobe IMS (avec `.error_code` et `.error_description`) |
| `TokenExpiredError`   | Le jeton d’actualisation a expiré ; l’utilisateur doit se réauthentifier                                     |
| `NetworkError`        | Délai d’attente HTTP ou échec de la connexion après toutes les tentatives                                    |
| `RateLimitError`      | Adobe IMS a renvoyé 429 ; vérifier `.retry_after` pour des recommandations sur le délai d’attente            |
| `PKCEError`           | Échec de la vérification PKCE (flux SPA)                                                                     |

### Gestion des jetons d’actualisation expirés en production

> **Warning**
>
> Pour les flux des applications web et des SPA, le jeton d’actualisation finira par expirer. Quand cela arrive, `get_token` déclenche `TokenExpiredError`. Vous devriez détecter cette situation et rediriger l’utilisateur vers le flux d’autorisation.

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

---

## Référence de configuration

Toutes les classes d’authentification acceptent ces paramètres facultatifs :

#### Référence de paramètre

| Paramètre            | Par défaut                             | Description                                                                                                                                                                                                                                                   |
| -------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `portées`            | Valeurs par défaut spécifiques au flux | Portées OAuth séparées par des espaces. Par défaut, l’authentification S2S est configurée sur `openid AdobeID frame.s2s.all` ; les flux destinés aux utilisateurs sont configurés par défaut sur `openid email profile offline_access additional_info.roles`. |
| `ims_base_url`       | `https://ims-na1.adobelogin.com`       | URL de base pour Adobe IMS. Remplacement pour les environnements de test ou hors production.                                                                                                                                                                  |
| `http_client`        | `Aucun`                                | `httpx personnalisé.Client` (ou `httpx.AsyncClient`) pour les proxys, le protocole mTLS ou la mise en pool des connexions.                                                                                                                                    |
| `délai d’expiration` | `30`                                   | Délai d’expiration en secondes des requêtes HTTP pour les appels vers le point d’entrée des jetons.                                                                                                                                                           |
| `max_retries`        | `2`                                    | Nombre maximal de tentatives en cas d’erreurs temporaires (codes 5xx, délais d’attente). Les tentatives de reconnexion en cas de dépassement de la limite de requêtes (429) sont comptabilisées séparément.                                                   |
| `refresh_buffer`     | `60`                                   | Secondes avant l’expiration du jeton pour déclencher une actualisation proactive.                                                                                                                                                                             |
| `on_token_refreshed` | `Aucun`                                | Rappel déclenché après chaque actualisation réussie du jeton. Reçoit un dictionnaire contenant `access_token`, `refresh_token` et `expires_at`.                                                                                                               |

### Environnements d’évaluation

Pointe vers une instance Adobe IMS d’évaluation en ignorant `ims_base_url`. Le SDK exporte également `DEFAULT_IMS_BASE_URL` (`https://ims-na1.adobelogin.com`) si vous devez accéder à la valeur de production par programmation.

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

### Client HTTP personnalisé

Pour l’assistance relative aux serveurs proxy ou la configuration TLS personnalisée :

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

---

## Sécurité des threads

Les classes d’authentification de synchronisation sont entièrement thread-safe. Lorsque plusieurs threads appellent `get_token` simultanément et qu’une actualisation est nécessaire, un seul thread effectue cette actualisation. Les autres attendent et obtiennent le même résultat. Aucun verrouillage externe n’est nécessaire. Les classes asynchrones offrent la même garantie en utilisant `asyncio.Lock`, compatible avec l’exécution simultanée de coroutines au sein d’une même boucle d’événements.