SDK Frame.io для Python — руководство по аутентификации

В этом руководстве объясняется, как выполнять аутентификацию с помощью API-интерфейса Frame.io, используя SDK Frame.io для Python (frameio). API-интерфейс Frame.io V4 использует службу Adobe Identity Management Service (IMS) — платформу идентификации Adobe на базе протокола OAuth 2.0. Это отдельное справочное руководство для Python-разработчиков. Все приведенные ниже примеры кода и рабочие процессы относятся исключительно к пакету frameio.


Типы аутентификации в SDK для Python

SDK для Python поддерживает четыре варианта аутентификации.

МетодПример использованияТребуется взаимодействие с пользователем?Требуется секретный ключ клиента?
Статический токенБыстрые сценарии, тестирование или если у вас уже есть токенНетНет
Межсерверная аутентификацияСерверные службы, задачи планировщика, автоматизацияНетДа
Веб-приложениеСерверные приложения (Flask, Django, FastAPI)ДаДа
Одностраничное приложение (PKCE)Браузерные приложения, интерфейсы CLI или любые приложения, которые не могут хранить секретный ключДаНет
Межсерверная аутентификация позволяет приложению действовать как сервисная учетная запись без участия пользователя. Доступна только для учетных записей Frame.io V4, управляемых через Adobe Admin Console. Процессы веб-приложения и одностраничного приложения позволяют приложению действовать от имени конкретного пользователя. Оба процесса используют архитектуру Adobe IMS: пользователь авторизует приложение, после чего SDK обменивает полученный код на токены. SDK для Python полностью берет на себя выполнение процессов IMS /authorize/v2 и /token/v3. Для веб-приложения требуется секретный ключ клиента, а для одностраничного приложения используется PKCE.

Учетные данные встроенного приложения Adobe требуют настройки пользовательских обработчиков схем URI (например, adobe+<hash>://…</hash>), которые перехватывают перенаправления на уровне ОС. В Python нет стандартного способа регистрации таких обработчиков, поэтому в SDK для Python отсутствует класс NativeAppAuth. Для интерактивных приложений Python используйте WebAppAuth с локальным сервером обратного вызова (например, Flask или FastAPI). Для неинтерактивных рабочих нагрузок используйте ServerToServerAuth.


Пользователи сервисных учетных записей

При использовании межсерверной аутентификации ваше приложение выступает в роли пользователя сервисной учетной записи — отдельного типа учетной записи, которая может выполнять действия от имени сервиса. Эти действия видны другим пользователям в Frame.io: когда сервисная учетная запись выполняет какое-либо действие, ее имя отображается в интерфейсе. Вы можете предоставлять и отзывать права доступа сервисных учетных записей через Adobe Admin Console и Developer Console. Управление именами сервисных учетных записей осуществляется через интерфейс Frame.io. По умолчанию ваше первое межсерверное подключение (S2S) называется Service Account User, второе — Service Account User 2 и так далее.


Быстрый старт

Предварительные требования

  1. Учетные данные из Adobe Developer Console
  • Client ID — требуется для всех процессов OAuth - секретный ключ клиента — требуется для процессов S2S и веб-приложения - URI перенаправления — требуется для процессов веб-приложения и одностраничного приложения (SPA); должен быть зарегистрирован в вашем проекте Adobe
  1. Установка SDK
pip install frameio

Выбор метода

  • Пользователь не участвует? Используйте межсерверную аутентификацию (ServerToServerAuth).
  • Пользователь участвует и вы можете хранить секретный ключ? Используйте веб-приложение (WebAppAuth).
  • Пользователь участвует, но вы не можете хранить секретный ключ? Используйте одностраничное приложение (SPAAuth).

Токен доступа

Если у вас уже есть токен доступа, полученный из другой системы OAuth или в результате предыдущего обмена (например, через наш API-интерфейс Explorer), вы можете передать его напрямую.

from frameio import Frameio
client = Frameio(token="YOUR_ACCESS_TOKEN")

Это самый простой подход, но срок действия токена со временем истечет, и SDK не будет обновлять его автоматически.

Устаревшие токены разработчика

Для учетных записей, переведенных на версию V4, которые еще не управляются через Adobe Admin Console, можно продолжать использовать устаревшие токены разработчика с сайта Frame.io Developer. При этом необходимо добавить заголовок x-frameio-legacy-token-auth и установить его значение на true.

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

Устаревшие токены разработчика не истекают, но они являются переходным механизмом. Для новых интеграций и производственных нагрузок мы рекомендуем использовать один из приведенных ниже процессов OAuth 2.0. Подробнее — в руководстве по миграции.


Межсерверная аутентификация (учетные данные клиента)

Используйте этот метод для серверных сервисов и сценариев, которым нужен доступ к Frame.io без участия пользователя. Этот процесс доступен только для учетных записей Frame.io V4, управляемых через Adobe Admin Console. Ваше приложение проходит аутентификацию как пользователь сервисной учетной записи без участия человека.

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)

Готово. auth.get_token — это вызываемый объект, который SDK запускает при каждом запросе. Если текущий токен все еще действителен, он возвращается мгновенно. Если срок его действия подходит к концу, метод сначала запрашивает новый токен без всякого вашего участия.

Как это работает

Ваши учетные данные клиента (идентификатор клиента + секретный ключ) не имеют срока действия. Вы меняете их только вручную в целях безопасности. Метод S2S обеспечивает фактически бессрочный и бесперебойный доступ к API-интерфейсу без какого-либо ручного вмешательства.

Архитектура процесса

  1. При первом вызове API-интерфейса метод get_token запрашивает новый токен доступа от Adobe IMS, используя тип разрешения client_credentials.
  2. Токен кэшируется в памяти. Срок действия отдельных токенов доступа ограничен (обычно 24 часа), но SDK берет управление этим процессом на себя.
  3. При попадании кэшированного токена в буфер обновления (по умолчанию за 60 секунд до истечения) SDK автоматически получает новый токен, используя те же учетные данные клиента.
  4. Токены обновления не используются. Сами учетные данные клиента являются долговременным секретным ключом, который всегда можно использовать для выпуска нового токена доступа.

Явная аутентификация

Если вы хотите получить токен заранее (например, чтобы быстро выявить ошибку на этапе запуска, если учетные данные неверны), используйте следующий метод.

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

Веб-приложение (код авторизации)

Используйте этот метод для серверных приложений, в которых пользователи выполняют вход с помощью Adobe ID. В этом процессе требуется секретный ключ клиента, который должен безопасно храниться на вашем сервере.

1

Перенаправление пользователя в Adobe IMS

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

Обработка обратного вызова

Когда Adobe IMS перенаправляет пользователя обратно на ваш redirect_uri, извлеките параметры code и state. Убедитесь в соответствии состояния тому, что вы сохранили, затем обменяйте код на токены.

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

Этот процесс производит обмен кода авторизации на токен доступа и токен обновления, сохраняя их оба во внутренней памяти.

3

Использование клиента

from frameio import Frameio
client = Frameio(token=auth.get_token)

Готово. С этого момента метод get_token автоматически управляет жизненным циклом токена. Когда срок действия токена доступа подходит к концу, SDK использует токен обновления для получения нового. Участия пользователя не требуется.

Полный пример реализации на Flask

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

Одностраничное приложение / PKCE (код авторизации + PKCE)

Используйте этот метод для браузерных приложений, программ для ПК или инструментов командной строки (CLI), которые не могут безопасно хранить секретный ключ клиента. В этом процессе используется расширение PKCE (RFC 7636), чтобы защитить обмен кода авторизации. <Steps>

<Step title=“Создание URL-адреса авторизации”>

<Tabs>

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

</div>Метод

get_authorization_url возвращает объект AuthorizationUrlResult, содержащий полный URL-адрес (с встроенной строкой PKCE code_challenge) и строку code_verifier, которая понадобится вам на следующем шаге. </Step>

1

Обмен кода с проверочной строкой

Когда пользователь перенаправляется обратно, используется следующий метод.

from frameio import Frameio
client = Frameio(token=auth.get_token)

Готово. Обновление работает так же, как и в процессе веб-приложения — SDK использует токен обновления автоматически. Разница заключается в том, что при обновлении не отправляется секретный ключ клиента, поскольку сценарий SPA разработан специально для публичных клиентов.

</Steps>


Асинхронное использование

Каждому классу аутентификации соответствует его асинхронный аналог с префиксом Async. Приведенные выше примеры кода содержат вкладки Синхронный и Асинхронный, где это применимо.

СинхронныйАсинхронный
ServerToServerAuthAsyncServerToServerAuth
WebAppAuthAsyncWebAppAuth
SPAAuthAsyncSPAAuth
API-интерфейс полностью идентичен. Метод get_authorization_url остается синхронным (без операции ввода-вывода), в то время как exchange_code, refresh, revoke и get_token являются асинхронными (async). Используйте асинхронные классы с AsyncFrameio.

Ручное обновление токена

В процессах веб-приложения и одностраничного приложения SDK обновляет токены автоматически с помощью метода get_token. Если вам нужен явный контроль над этим процессом, вы можете вызвать метод refresh() напрямую.

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

Это полезно, когда нужно принудительно обновить токен перед выполнением критически важной операции, вместо того чтобы полагаться на автоматический буфер обновления.


Сохранение токенов

Все классы аутентификации поддерживают методы export_tokens() и import_tokens() для сохранения состояния токенов между перезапусками приложения. Для процессов веб-приложения и одностраничного приложения это особенно важно, поскольку токены доступа и обновления по умолчанию хранятся в памяти. Если ваше приложение перезапустится, пользователям придется проходить аутентификацию заново, если только вы не сохранили токены. Для межсерверной аутентификации сохранение необязательно (учетные данные клиента всегда могут выпустить новый токен), но импорт кэшированного токена позволяет избежать лишнего цикла запроса-ответа при запуске.

Экспорт и импорт

# 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

Автоматическое сохранение с помощью функции on_token_refreshed

Чтобы автоматически сохранять токены при каждом их обновлении, используйте функцию обратного вызова 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()))

Функция обратного вызова принимает словарь того же формата, что и export_tokens(), и срабатывает после каждого успешного обновления токена. Для асинхронных классов on_token_refreshed может быть как обычной функцией, так и асинхронной (async). Поддерживаются оба варианта.


Отзыв токенов

Чтобы выполнить выход пользователя из системы и аннулировать его токены в Adobe IMS, используется следующий метод.

auth.revoke()

Этот метод отправляет в Adobe IMS запрос на аннулирование токена доступа и токена обновления по принципу наилучших усилий, а затем очищает все локальные данные о состоянии токенов. После отзыва токенов пользователю потребуется пройти аутентификацию заново.

Для асинхронных классов используйте await auth.revoke().


Обработка ошибок

Все ошибки аутентификации наследуются от FrameioAuthError, поэтому вы можете перехватывать их в общем блоке или обрабатывать конкретные случаи отдельно.

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

Справочник по ошибкам

ИсключениеКогда возникает
ConfigurationErrorОтсутствует или указана неверная конфигурация (например, пустой client_id, URI перенаправления без поддержки HTTPS).
AuthenticationErrorОбмен или обновление токена отклонены со стороны Adobe IMS (содержит .error_code и .error_description).
TokenExpiredErrorИстек срок действия самого токена обновления; пользователю необходимо пройти аутентификацию заново.
NetworkErrorИстекло время ожидания HTTP-запроса или произошел сбой подключения после исчерпания всех повторных попыток.
RateLimitErrorСервис Adobe IMS вернул ошибку 429; используйте .retry_after для определения времени задержки перед повторным запросом.
PKCEErrorСбой проверки PKCE (процесс SPA).

Обработка истекших токенов обновления в производстве

В процессах веб-приложения и SPA срок действия токена обновления со временем истекает. Когда это происходит, метод get_token генерирует исключение TokenExpiredError. Вам следует перехватить эту ошибку и перенаправить пользователя на повторное прохождение процедуры авторизации.

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

Справочник по конфигурации

Все классы аутентификации принимают следующие дополнительные параметры.

ПараметрПо умолчаниюОписание
scopesЗависит от выбранного процессаОбласти доступа OAuth, разделенные пробелами. Для сценария S2S по умолчанию используются значения openid AdobeID frame.s2s.all; для пользовательских процессов — openid email profile offline_access additional_info.roles.
ims_base_urlhttps://ims-na1.adobelogin.comБазовый URL-адрес Adobe IMS. Переопределите его для работы в тестовых или непроизводственных средах.
http_clientНетПользовательский клиент httpx.Client (или httpx.AsyncClient) для прокси, mTLS или пула подключений.
timeout30Время ожидания запроса HTTP в секундах при вызове конечных точек токенов.
max_retries2Максимальное количество повторных попыток при временных сбоях (ошибки 5xx, таймауты). Повторные попытки при превышении лимита запросов (ошибки 429) отслеживаются отдельно.
refresh_buffer60Количество секунд до истечения срока действия токена, при котором запускается упреждающее обновление.
on_token_refreshedНетФункция обратного вызова, которая срабатывает после каждого успешного обновления токена. Принимает словарь, содержащий access_token, refresh_token и expires_at.

Тестовые среды

Чтобы перенаправить запросы на тестовый экземпляр Adobe IMS, переопределите параметр ims_base_url. SDK также экспортирует константу DEFAULT_IMS_BASE_URL (https://ims-na1.adobelogin.com) если вам необходимо программно ссылаться на значение, используемое в производственной среде.

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

Пользовательский HTTP-клиент

Для поддержки прокси или пользовательской конфигурации TLS используйте следующий метод.

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

Безопасность потоков

Синхронные классы аутентификации полностью потокобезопасны. Если несколько потоков одновременно вызывают метод get_token в момент, когда требуется обновление, только один поток выполняет это обновление. Остальные потоки ожидают и получают тот же результат. Никаких внешних механизмов блокировки не требуется. Асинхронные классы обеспечивают такую же гарантию безопасности с помощью механизма asyncio.Lock, что делает их безопасными для одновременного использования несколькими сопрограммами в рамках одного цикла событий.