Frame.io Python SDK — 身份验证指南

本指南介绍如何使用 Frame.io Python SDK (frameio) 通过 Frame.io API 进行身份验证。 Frame.io V4 API 使用 Adobe Identity Management Service (IMS),Adobe 的 OAuth 2.0 身份平台。 这是面向 Python 开发者的独立参考。 下方的所有代码示例和流程仅适用于 frameio 包。


Python SDK 中的身份验证类型

Python SDK 支持四种身份验证选项:

方法用例是否需要用户交互?是否需要客户端密钥?
静态令牌快速脚本、测试,或者您已拥有令牌否否
服务器到服务器后端服务、定时任务、自动化否是
Web 应用程序服务器端应用程序(Flask、Django、FastAPI)是是
SPA (PKCE)浏览器应用程序、CLI 或任何无法存储密钥的应用程序是否
服务器到服务器方式让您的应用程序以服务帐户身份运行,无需用户交互。此方式仅适用于通过 Adobe Admin Console 管理的 Frame.io V4 帐户。Web 应用程序和 SPA 方式让您的应用程序以特定用户的身份运行。两者底层都使用 Adobe IMS:用户授权您的应用程序后,SDK 会将生成的代码交换为令牌。Python SDK 为您处理 IMS 的 /authorize/v2 和 /token/v3 流程。对于 Web 应用程序,您需要客户端密钥;对于 SPA,则改用 PKCE。

Adobe 的原生应用程序凭据需要自定义 URI 方案处理程序(例如 adobe+<hash>://…</hash>),这些处理程序会在操作系统层面拦截重定向。Python 没有注册此类处理程序的标准方法,因此 Python SDK 不提供 NativeAppAuth 类。对于需要用户交互的 Python 应用程序,请使用 WebAppAuth 并搭配本地回调服务器(例如 Flask 或 FastAPI)。对于非交互式工作负载,请使用 ServerToServerAuth。


服务帐户用户

使用服务器到服务器的身份验证时,您的应用程序充当服务帐户用户,这是一种可以代表服务执行操作的独特帐户类型。 这些内容在 Frame.io 中对其他用户可见:当服务帐户执行操作时,其名称会显示在 UI 中。 您可以通过 Adobe Admin Console 和 Developer Console 授予和撤销服务帐户访问权限。 服务帐户名称通过 Frame.io UI 进行管理。 默认情况下,您的第一个 S2S 连接名为 Service Account User,第二个名为 Service Account User 2,以此类推。


快速入门

前提条件

  1. Adobe Developer Console 提供的 凭据:
  • 客户端 ID — 所有 OAuth 流均需要 - 客户端密钥 — 服务器到服务器的流和 Web 应用程序流需要 - 重定向 URI — Web 应用程序流和 SPA 流需要;必须注册在 Adobe 项目中
  1. 安装 SDK:
pip install frameio

选择方法

  • 无用户参与? 使用服务器到服务器 (ServerToServerAuth)。
  • 有用户参与且您可以存储密钥? 使用 Web 应用程序 (WebAppAuth)。
  • 有用户参与但您无法存储密钥? 使用 SPA (SPAAuth)。

访问令牌

如果您已有访问令牌(来自其他 OAuth 系统或之前的交换,例如通过我们的 API Explorer获得),那么您可以直接传递:

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

这是最简单的方法,但令牌最终会过期,SDK 不会为您刷新令牌。

旧版开发者令牌

对于尚未通过 Adobe Admin Console 进行管理的 V4 迁移帐户,您可以继续使用来自 Frame.io 开发者网站的旧版开发者令牌。 您必须包含 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 访问权限且无需用户交互的后端服务和脚本。 此流仅适用于通过 Adobe Admin Console 管理的 Frame.io V4 帐户。 您的应用程序以服务帐户用户的身份进行身份验证,无需人工干预。

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 在每个请求中都会调用的一个可调用对象。 如果当前令牌仍然有效,它就会立即返回。 如果即将过期,它会先获取一个新令牌,过程完全透明。

工作原理

您的客户端凭据(客户端 ID + 密钥)永不过期。 您只需出于安全维护目的手动轮换它们即可。 S2S 可为您提供实际上永久、不间断的 API 访问权限,无需手动干预。

底层原理:

  1. 对于第一次 API 调用,get_token 使用 client_credentials 授权凭证从 Adobe IMS 请求新的访问令牌。
  2. 令牌会缓存在内存中。 单个访问令牌会过期(通常为 24 小时),但这会为您自动处理。
  3. 当缓存的令牌在刷新缓冲时间内时(默认:过期时间前 60 秒),SDK 会使用相同的客户端凭据自动获取新的令牌。
  4. 不涉及刷新令牌。 客户端凭据本身就是长期有效的密钥,始终可用于生成新的访问令牌。

显式身份验证

如果您想主动获取令牌(例如,在启动时因错误凭据而快速失败):

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

Web 应用程序(授权代码)

将此用于用户使用其 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) 来保护授权代码交换。

1

生成授权 URL

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 返回一个 AuthorizationUrlResult,其中包含完整 URL(嵌入 PKCE code_challenge)以及您在下一步中需要的 code_verifier。

2

使用验证器交换代码

当用户被重定向回来时:

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

使用客户端

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

就是这样。 刷新操作与 Web 应用程序相同 — SDK 会自动使用刷新令牌。 区别在于,刷新期间不会发送客户端密钥,因为 SPA 流专为公共客户端而设计。


异步用法

每个身份验证类都有一个异步对应项,以 Async 为前缀。 上述代码示例包含适用的 Sync 和 Async 选项卡。

同步异步
ServerToServerAuthAsyncServerToServerAuth
WebAppAuthAsyncWebAppAuth
SPAAuthAsyncSPAAuth
API 完全相同。 get_authorization_url 保持同步(无 I/O),而 exchange_code、refresh、revoke 和 get_token 都是 async。 将异步类与 AsyncFrameio 一起使用。

手动令牌刷新

对于 Web 应用程序和 SPA 流,SDK 通过 get_token 自动刷新令牌。 如果您需要显式控制,可以直接调用 refresh():

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

当您希望在关键操作之前强制刷新而不是依赖自动刷新缓冲区时,这样做非常有用。


令牌持久性

所有身份验证类都支持 export_tokens() 和 import_tokens() 来在重新启动时持久化令牌状态。 对于 Web 应用程序和 SPA 流程,这尤其重要,因为访问令牌和刷新令牌默认保存在内存中 — 如果您的应用程序重新启动,用户需要重新进行身份验证,除非您对这些令牌进行持久化。 对于服务器到服务器,持久性是可选的(客户端凭据始终可以生成新令牌),但导入缓存的令牌可以避免启动时出现额外的往返。

导出和导入

# 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 为空,非 HTTPS 重定向 URI)
AuthenticationErrorAdobe IMS 拒绝令牌交换或刷新(具有 .error_code 和 .error_description)
TokenExpiredError刷新令牌本身已过期;用户必须重新进行身份验证
NetworkErrorHTTP 超时或完成所有重试后仍出现连接故障
RateLimitErrorAdobe IMS 返回 429;查看 .retry_after 获取退避指导
PKCEErrorPKCE 验证失败(SPA 流)

在生产中处理过期的刷新令牌

对于 Web 应用程序和 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.comAdobe IMS 基础 URL。 针对暂存或非生产环境的覆盖设置。
http_client无自定义 httpx。用于代理、mTLS 或连接池的客户端(或 httpx.AsyncClient)。
timeout30令牌端点调用的 HTTP 请求超时时间(以秒为单位)。
max_retries2瞬态故障(5xx、超时)的最大重试次数。 速率限制重试 (429) 单独跟踪。
refresh_buffer60令牌过期时间前触发主动刷新的秒数。
on_token_refreshed无每次成功刷新令牌后触发的回调。 接收包含 access_token、refresh_token 和 expires_at 的字典。

暂存环境

通过重写 ims_base_url 指向暂存的 Adobe IMS 实例。 如果您需要以编程方式引用生产值,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 提供相同的保证,对单个事件循环中的并发协程是安全的。