> This page is for 平台, version 旧版.
> For other versions, use one of these documentation indexes:
> - V4 (default): https://next.developer.frame.io/platform/v4/llms.txt
> - V4 实验版: https://next.developer.frame.io/platform/v4-experimental/llms.txt
> - 旧版: 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.

# 刷新 OAuth 2 令牌

<Warning title="本指南假定您已经构建了一个 OAuth2 应用程序">
  如果尚未构建，请参阅[该](/oauth-2-applications/building-an-oauth-2-app)指南，并在从成功的 OAuth 2 凭据授权中捕获 `access_token` 和 `refresh_token` 之后再返回此处。
</Warning>


## 令牌刷新基础知识

假设您在 OAuth2.0 凭据请求中包含了 ***offline*** 权限范围，那么通过 Frame.io 的帐户应用程序成功完成验证身份后将返回一个如下所示的负载：

```json
{
  "access_token":"BEARER_TOKEN",
  "expires_in":3600,
  "refresh_token":"REFRESH_TOKEN",
  "scope":"account.read offline",
  "token_type":"bearer"
}
```

`access_token` 是一个持有者令牌，可用于代表经过身份验证的用户执行操作；它将在 3600 秒（一小时）后过期；在此之后，可以使用 `refresh_token` 来获取新的 `access_token`。刷新令牌随后将在 30 天后过期，届时您需要让用户重新登录，生成一对新的访问/刷新令牌；如此循环。如果您未明确请求 *offline* 权限范围，则不会收到 `refresh_token`，因此一小时后您将需要让用户完全重新进行身份验证。

### 成功完成身份验证时捕获刷新令牌

不言而喻，您无法使用您没有的 `refresh_token`，因此请确保在您的应用程序中：
* 请求 **offline** 权限范围
* 捕获成功回调中返回的 `refresh_token`。

为方便起见，此处重现了我们 [OAuth 2 应用程序指南](/oauth-2-applications/building-an-oauth-2-app)中的回调，其中包含一个用于存储刷新令牌的 `os` 调用。请注意，下面提供了两个示例：一个配置了 PKCE（不包括基本身份验证标头），另一个未配置（包括基本身份验证标头）。

### 不使用 PKCE




**`Python`**

```python title="Python"
def callback():
  # Where `request` refers to our initial call to the auth URL
  state = request.args.get('state')
  scope = request.args.get('scope')
  code = request.args.get('code')
  error = request.args.get('error')

  if error:
    return "Error: " + error

  # Set up for client authorization and set up the data you need to send.
  client_auth = requests.auth.HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET)

  post_data = {
    "grant_type": "authorization_code",
    "code": code,
    "redirect_uri": REDIRECT_URI,
    "state": state,
    "scope": SCOPE
  }

  # Send a POST request with the data you need to receive an access token. 
  response = requests.post(TOKEN, auth=client_auth, data=post_data)    
	# Stash the refresh token for later
  os.environ['REFRESH_TOKEN'] = response.json()["refresh_token"]

  return response.text
```





### 使用 PKCE




**`Python`**

```python title="Python"
def callback():
  # Where `request` refers to our initial call to the auth URL
  state = request.args.get('state')
  scope = request.args.get('scope')
  code = request.args.get('code')
  error = request.args.get('error')

  if error:
    return "Error: " + error

  # If using PKCE, you must include the CLIENT_ID in your request body  
  post_data = {
    "grant_type": "authorization_code",
    "code": code,
    "redirect_uri": REDIRECT_URI,
    "state": state,
    "scope": SCOPE
    "client_id": CLIENT_ID 
  }

  # Send a POST request with the data you need to receive an access token.
  # If using PKCE, use the below request with no auth
  response = requests.post(TOKEN_URL, data=post_data)
  # Stash the refresh token for later
  os.environ['REFRESH_TOKEN'] = response.json()["refresh_token"]

  return response.text
```




## 执行刷新




刷新本身是对 Frame.io 令牌 URL 的一次单一调用：



* 方法：POST
* URL：**https://applications.frame.io/oauth2/token**
* `Content-Type`：*application/x-www-form-urlencoded*




一次刷新在其表单数据中始终至少包含以下三个属性：



* `grant_type`：*refresh_token*
* `scope`：\<scopes>
* `refresh_token`：\<refresh_token>

如果您使用的是 PKCE，则需要在此表单数据中包含应用程序的 `client_id`；如果不使用，则需要包含一个基本身份验证标头，并分别以应用程序的 `client_id` 和 `client_secret` 作为用户名和密码。

### 不使用 PKCE

与不使用 PKCE 时[进行初始身份验证回调](/oauth-2-applications/building-an-oauth-2-app#the-callback)类似，此标准刷新需要在基本身份验证标头中提供 `client_id` 和 `client_secret`，分别作为用户名和密码。

**`Python`**

```python title="Python"
def refresh():
  # Fetch the refresh token, assuming we have it
  REFRESH_TOKEN = os.environ.get('REFRESH_TOKEN')

  client_auth = requests.auth.HTTPBasicAuth(CLIENT_ID,CLIENT_SECRET)
  post_data = {
    "grant_type": "refresh_token",
    "scope": SCOPE,
    "refresh_token": REFRESH_TOKEN
    # if using PKCE, you will need to include your client_id as below
    # "client_id": CLIENT_ID 
  }

  response = requests.post(TOKEN_URL, auth=client_auth, data=post_data)
  # Catch + stash a new Refresh Token
  os.environ['REFRESH_TOKEN'] = response.json()["refresh_token"]

  return response.text
```




### 使用 PKCE

同样，我们遵循初始 `/callback` 循环的规则：
* 我们不包含 `Authorization` 标头
* 我们必须在负载中包含 `client_id`




**`Python`**

```python title="Python"
def refresh():
  # Fetch the refresh token, assuming we have it
  REFRESH_TOKEN = os.environ.get('REFRESH_TOKEN')

  post_data = {
    "grant_type": "refresh_token",
    "scope": SCOPE,
    "refresh_token": REFRESH_TOKEN
    "client_id": CLIENT_ID 
  }

  response = requests.post(TOKEN_URL, data=post_data)
  # Catch + stash a new Refresh Token
  os.environ['REFRESH_TOKEN'] = response.json()["refresh_token"]

  return response.text
```




祝贺您！现在，您可以处理 OAuth2.0 客户端应用程序的整个令牌生命周期了。