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), 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éthodeCas d’usageInteraction utilisateur ?Nécessite un secret client ?
Jeton statiqueScripts rapides, test ou vous avez déjà un jetonNonNon
De serveur à serveurServices backend, tâches cron, automatisationNonOui
Application webApplications côté serveur (Flask, Django, FastAPI)OuiOui
SPA (PKCE)Applications web, interfaces de ligne de commande (CLI) ou toute application qui ne peut pas stocker un secretOuiNon
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. 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 à la place.

L’option Informations d’identification pour une application native 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 et la Developer 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.


Démarrage rapide

Conditions préalables

  1. Informations d’identification de l’Adobe Developer 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.
  1. Installer le SDK :
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), vous pouvez le transmettre directement :

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, vous pouvez continuer à utiliser les jetons de développeur hérités disponibles sur le site des développeurs Frame.io. Vous devez inclure l’en-tête x-frameio-legacy-token-auth et le configurer sur true :

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.


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. Votre application s’authentifie en tant qu’utilisateur de compte de service sans intervention humaine.

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)

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

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.

1

Rediriger l’utilisateur vers Adobe IMS

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

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 :

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

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.

3

Utiliser le client

from frameio import Frameio
client = Frameio(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

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) pour sécuriser l’échange de codes d’autorisation.

1

Générer l’URL d’autorisation

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

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.

2

Échanger le code avec le vérificateur

Lorsque l’utilisateur est redirigé de retour :

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

Utiliser le client

from frameio import Frameio
client = Frameio(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.

SyncAsync
ServerToServerAuthAsyncServerToServerAuth
WebAppAuthAsyncWebAppAuth
SPAAuthAsyncSPAAuth
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 :

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

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

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

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 :

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.

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 :

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

ExceptionDéclenchée dans les cas suivants
ConfigurationErrorConfiguration manquante ou non valide (p. ex. client_id vide, URI de redirection non HTTPS)
AuthenticationErrorRefus de l’échange ou de l’actualisation du jeton par Adobe IMS (avec .error_code et .error_description)
TokenExpiredErrorLe jeton d’actualisation a expiré ; l’utilisateur doit se réauthentifier
NetworkErrorDélai d’attente HTTP ou échec de la connexion après toutes les tentatives
RateLimitErrorAdobe 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

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.

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 :

ParamètrePar défautDescription
portéesValeurs par défaut spécifiques au fluxPorté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_urlhttps://ims-na1.adobelogin.comURL de base pour Adobe IMS. Remplacement pour les environnements de test ou hors production.
http_clientAucunhttpx personnalisé.Client (ou httpx.AsyncClient) pour les proxys, le protocole mTLS ou la mise en pool des connexions.
délai d’expiration30Délai d’expiration en secondes des requêtes HTTP pour les appels vers le point d’entrée des jetons.
max_retries2Nombre 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_buffer60Secondes avant l’expiration du jeton pour déclencher une actualisation proactive.
on_token_refreshedAucunRappel 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.

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 :

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.