40 KiB
获取授权码
本接口用于发起用户授权,应用在用户同意授权后将获得授权码 code。请注意授权码的有效期为 5 分钟,且只能被使用一次。
注意事项
-
本接口实际为授权页面,适用于网页应用的授权场景。在需要用户授权时,应用应将用户重定向至本授权页面。当用户在授权页面点击授权后(在飞书客户端内打开网页应用时可免确认直接跳转),浏览器将跳转至
redirect_uri所指定的地址,并携带code查询参数(即授权码)。 -
开发者可通过授权码获取
user_access_token,以调用 OpenAPI(例如获取用户信息)。有关获取user_access_token的详细步骤,可参考获取 user_access_token。 -
通过本接口配合获取 user_access_token以及获取用户信息,应用可实现飞书授权登录。
-
完整的用户授权链路实现,可参考浏览器应用接入指南。
-
在打开授权页面时,需要通过拼接
scope查询参数声明应用所需的用户授权权限。例如,获取通讯录基本信息的权限对应的scope键为contact:contact.base:readonly。 -
用户授予应用的权限是累积的,最新生成的
user_access_token将包含用户历史上已授予的所有权限。 -
当应用使用
user_access_token调用某个 OpenAPI 时,必须确保该user_access_token具备目标 OpenAPI 所需的权限,否则调用将失败。
请求
| 基本 | |
|---|---|
| HTTP URL | https://accounts.feishu.cn/open-apis/authen/v1/authorize |
| HTTP Method | GET |
| 接口频率限制 | 1000 次/分钟、50 次/秒 |
| 支持的应用类型 | Custom App、Store App |
| 权限要求 调用该 API 所需的权限。开启其中任意一项权限即可调用 |
无 |
查询参数
为了确保 URL 构造 & 编码正确,建议使用相关的 URL 标准库来完成 URL 的解析和构建,避免手动拼接。
| 名称 | 类型 | 必填 | 描述 |
|---|---|---|---|
| client_id | string | 是 | 应用的 App ID,可以在开发者后台的凭证与基础信息页面查看 App ID。有关 App ID 的详细介绍,请参考通用参数。 示例值: cli_a5d611352af9d00b |
| response_type | string | 是 | 应用通知授权服务器所需的授权类型,对于授权码流程,固定值code 示例值: code |
| redirect_uri | string | 是 | 应用重定向地址,在用户授权成功后会跳转至该地址,同时会携带 code 以及 state 参数(如有传递 state 参数)。请注意: 1. 该地址需经过 URL 编码; 2. 调用本接口前,你需要在开发者后台应用的安全设置页面,将用于接受 OAuth 回调的 HTTP GET 请求接口地址配置为应用的重定向 URL。重定向 URL 支持配置多个,只有在重定向 URL 列表中的 URL 才会通过开放平台的安全校验。详情请参考配置重定向域名。 示例值: https://example.com/api/oauth/callback |
| scope | string | 否 | 用户需要增量授予应用的权限。 格式要求: scope 参数为空格分隔,区分大小写的字符串。注意: - 开发者需要根据业务场景,在开发者后台的 权限管理 模块中完成调用 OpenAPI 所需的 scope 申请后,自主拼接 scope 参数。如果没有在应用后台为应用申请相应权限,则实际使用应用时用户会遇到 20027 报错。- 应用最多一次可以请求用户授予 50 个 scope。详情参考 API 权限列表。- 如果后续需要获取 refresh_token,此处需要添加 offline_access 权限。详情参考 刷新 user_access_token):offline_access(offline_access) 示例值: contact:contact bitable:app:readonly |
| state | string | 否 | 用来维护请求和回调之间状态的附加字符串,在授权完成回调时会原样回传此参数。应用可以根据此字符串来判断上下文关系,同时该参数也可以用以防止 CSRF 攻击,请务必校验 state 参数前后是否一致。示例值: RANDOMSTRING |
| code_challenge | string | 否 | 用于通过 PKCE(Proof Key for Code Exchange)流程增强授权码的安全性。 示例值: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM有关 PKCE 的详细信息,请参阅RFC-7636 - Proof Key for Code Exchange by OAuth Public Clients。 |
| code_challenge_method | string | 否 | 生成 code_challenge 的方法。可选值: 1. S256(推荐): 使用 SHA-256 哈希算法计算 code_verifier 的哈希值,并将结果进行 Base64URL 编码,生成 code_challenge。 2. plain(默认值): 直接将 code_verifier 作为 code_challenge,无需进行额外处理。以上 code_verifier 是指在发起授权前,本地生成的随机字符串。 |
请求示例
注意仅为示例请求 URL,请根据前文描述将其中的查询参数替换为真实的值
https://accounts.feishu.cn/open-apis/authen/v1/authorize?client_id=cli_a5d611352af9d00b&response_type=code&redirect_uri=https%3A%2F%2Fexample.com%2Fapi%2Foauth%2Fcallback&scope=bitable:app:readonly%20contact:contact&state=RANDOMSTRING
响应
成功响应
当用户同意授权后,浏览器将重定向至发起授权时给定的的 redirect_uri 地址,同时携带 code 和 state 参数。
| 名称 | 描述 |
|---|---|
| code | 授权码,用于获取 user_access_token。字符集: [A-Z] / [a-z] / [0-9] / "-" / "_" 长度: 请开发者至少预留 64 位字符 示例值: 2Wd5g337vo5BZXUz-3W5KECsWUmIzJ_FJ1eFD59fD1AJIibIZljTu3OLK-HP_UI1 |
| state | 打开授权页时传入的 state 参数的原值,如未传入此处不会返回。 |
示例:
https://example.com/api/oauth/callback?code=2Wd5g337vo5BZXUz-3W5KECsWUmIzJ_FJ1eFD59fD1AJIibIZljTu3OLK-HP_UI1&state=RANDOMSTRING
失败响应
当用户拒绝授权时,浏览器将重定向至发起授权时给定的 redirect_uri 地址,同时携带 error 和 state 查询参数。 当前 error 参数的固定值为 access_denied,请妥善处理拒绝授权时的情况。
| 名称 | 描述 |
|---|---|
| error | 错误信息,当前固定为 access_denied |
| state | 打开授权页时传入的 state 参数的原值,如未传入此处不会返回 |
示例:
https://example.com/api/oauth/callback?error=access_denied&state=RANDOMSTRING
常见问题
用户授权应用时报错 20027
问题现象:用户在授权应用时报错 20027
问题原因:打开授权页时拼接的 scope 参数中包含当前应用未开通的权限。
解决方案:
- 确认需要用户授权的权限范围。
- 前往开发者后台,在对应应用的 开发配置 > 权限管理 > API 权限 功能页开通相应的权限。具体操作参考申请 API 权限。
- 调用当前接口,自主拼接已在应用内开通的权限。
如何获取包含目标权限的 user_access_token
在调用 OpenAPI 时,如果 user_access_token 缺少所需权限,将会返回以下错误:
{
"code": 99991679,
"error": {
"log_id": "202407260711088FB107A76E0100002087",
"permission_violations": [
{
"subject": "task:task:read",
"type": "action_privilege_required"
},
{
"subject": "task:task:write",
"type": "action_privilege_required"
}
]
},
"msg": "Unauthorized. You do not have permission to perform the requested operation on the resource. Please request user re-authorization and try again. required one of these privileges: [task:task:read, task:task:write]"
}
为避免因 user_access_token 权限不足导致 OpenAPI 调用失败,开发者可通过 scope 查询参数请求用户授予相应权限,具体有以下两种方式:
- 一次性拼接所有需要用户授权的
scope,在无新增权限需求的情况下,无需重复授权。需注意单次拼接的scope数量上限为 50 个。 - 或者,根据 OpenAPI 调用返回的错误码及
permission_violations字段,识别当前操作所需的额外权限。随后可重新生成授权链接,提示用户补充授权,并使用新生成的user_access_token继续调用 OpenAPI。
建议开发者遵循最小权限原则,仅要求用户授予必要的权限。
redirect_uri 中带 # 时的说明
标准 RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax中约定,URI 中 # 后面的内容称为 fragment,位置处于 URI 最后。如果业务授权请求参数 redirect_uri 拼接了 #,授权完成后的重定向会将 # 和 fragment 内容拼接到 URI 最后。业务在解析获取 code 时需要特别注意。
redirect_uri 示例:
https://example.com/api/oauth/callback/#/login
请求示例:
GET https://accounts.feishu.cn/open-apis/authen/v1/authorize?client_id=cli_a5d611352af9d00b&redirect_uri=https%3A%2F%2Fexample.com%2Fapi%2Foauth%2Fcallback%2F%23%2Flogin%0A&scope=bitable:app:readonly%20contact:contact&state=RANDOMSTRING
回调后浏览器地址栏中的值示例:
https://example.com/api/oauth/callback?code=2Wd5g337vo5BZXUz-3W5KECsWUmIzJ_FJ1eFD59fD1AJIibIZljTu3OLK-HP_UI1&state=RANDOMSTRING#/login
获取 user_access_token
OAuth 令牌接口,可用于获取 user_access_token 以及 refresh_token。user_access_token 为用户访问凭证,使用该凭证可以以用户身份调用 OpenAPI。refresh_token 为刷新凭证,可以用来获取新的 user_access_token。
- 获取
user_access_token前需要先获取授权码,详见获取授权码。请注意授权码的有效期为 5 分钟,且只能被使用一次。 - 用户授权时,用户必须拥有应用的使用权限,否则调用本接口将会报错误码 20010。
- 获取到的
user_access_token存在有效期,如何刷新user_access_token详见刷新 user_access_token。 - 如果你需要获取用户信息,详见获取用户信息。 注意事项:本接口实现遵循 RFC 6749 - The OAuth 2.0 Authorization Framework ,你可以使用标准的 OAuth 客户端库进行接入(推荐)
请求
| 基本 | |
|---|---|
| HTTP URL | https://open.feishu.cn/open-apis/authen/v2/oauth/token |
| HTTP Method | POST |
| 接口频率限制 | 1000 次/分钟、50 次/秒 |
| 支持的应用类型 | Custom App、Store App |
| 权限要求 调用该 API 所需的权限。开启其中任意一项权限即可调用 |
无 |
| 字段权限要求 | refresh_token 以及 refresh_token_expires_in 字段仅在具备以下权限时返回:offline_access(offline_access) |
请求头
| 名称 | 类型 | 必填 | 描述 |
|---|---|---|---|
| Content-Type | string | 是 | 请求体类型。 固定值: application/json; charset=utf-8 |
请求体
| 名称 | 类型 | 必填 | 描述 |
|---|---|---|---|
| grant_type | string | 是 | 授权类型。 固定值: authorization_code |
| client_id | string | 是 | 应用的 App ID。应用凭证 App ID 和 App Secret 获取方式: 1. 登录飞书开发者后台。 2. 进入应用详情页,在左侧导航栏,单击 凭证与基础信息。 3. 在 应用凭证 区域,获取并保存 App ID 和 App Secret。 示例值: cli_a5ca35a685b0x26e |
| client_secret | string | 是 | 应用的 App Secret。应用凭证 App ID 和 App Secret 获取方式: 1. 登录飞书开发者后台。 2. 进入应用详情页,在左侧导航栏,单击 凭证与基础信息。 3. 在 应用凭证 区域,获取并保存 App ID 和 App Secret。 示例值: baBqE5um9LbFGDy3X7LcfxQX1sqpXlwy |
| code | string | 是 | 授权码,详见获取授权码。 示例值: a61hb967bd094dge949h79bbexd16dfe |
| redirect_uri | string | 否 | 在构造授权页页面链接时所拼接的应用回调地址。 示例值: https://example.com/api/oauth/callback |
| code_verifier | string | 否 | 在发起授权前,本地生成的随机字符串,用于 PKCE(Proof Key for Code Exchange)流程。使用 PKCE 时,该值为必填项。 有关 PKCE 的详细介绍,请参阅 RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients。 长度限制: 最短 43 字符,最长 128 字符 可用字符集: [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" 示例值: TxYmzM4PHLBlqm5NtnCmwxMH8mFlRWl_ipie3O0aVzo |
| scope | string | 否 | 该参数用于缩减 user_access_token 的权限范围。例如: 1. 在获取授权码时通过 scope 参数授权了 contact:user.base:readonly contact:contact.base:readonly contact:user.employee:readonly 三个权限。2. 在当前接口可通过 scope 参数传入 contact:user.base:readonly,将 user_access_token 的权限缩减为 contact:user.base:readonly 这一个。注意: - 如果不指定当前参数,生成的 user_access_token 将包含用户授权时的所有权限。- 当前参数不能传入重复的权限,否则会接口调用会报错,返回错误码 20067。 - 当前参数不能传入未授权的权限(即获取授权码时用户已授权范围外的其他权限),否则接口调用会报错,返回错误码 20068。 - 多次调用当前接口缩减权限的范围不会叠加。例如,用户授予了权限 A 和 B,第一次调用该接口缩减为权限 A,则 user_access_token 只包含权限 A;第二次调用该接口缩减为权限 B,则 user_access_token 只包含权限 B。 - 生效的权限列表可通过本接口返回值 scope 查看。 格式要求: 以空格分隔的 scope 列表示例值: auth:user.id:read task:task:read |
请求体示例
{
"grant_type": "authorization_code",
"client_id": "cli_a5ca35a685b0x26e",
"client_secret": "baBqE5um9LbFGDy3X7LcfxQX1sqpXlwy",
"code": "a61hb967bd094dge949h79bbexd16dfe",
"redirect_uri": "https://example.com/api/oauth/callback",
"code_verifier": "TxYmzM4PHLBlqm5NtnCmwxMH8mFlRWl_ipie3O0aVzo"
}
响应
响应体类型为 application/json; charset=utf-8。
响应体
注意事项:响应体中的 access_token 和 refresh_token 长度较长,一般在 1~2KB 之间,且可能由于 scope 数量的变多或后续变更导致长度进一步增加,建议预留 4KB 的存储容量
| 名称 | 类型 | 描述 |
|---|---|---|
| code | int | 错误码,为 0 时表明请求成功,非 0 表示失败,请参照下文错误码一节进行相应处理 |
| access_token | string | 即 user_access_token,仅在请求成功时返回 |
| expires_in | int | 即 user_access_token 的有效期,单位为秒,仅在请求成功时返回注意事项:建议使用该字段以确定 user_access_token 的过期时间,不要硬编码有效期 |
| refresh_token | string | 用于刷新 user_access_token,详见刷新 user_access_token。该字段仅在请求成功且用户授予 offline_access 权限时返回。注意事项:如果你在获取 user_access_token 时设置了 scope 请求参数,且需要返回 refresh_token,则需要在 scope 参数中包括 offline_access。另外,refresh_token 仅能被使用一次。 |
| refresh_token_expires_in | int | 即 refresh_token 的有效期,单位为秒,仅在返回 refresh_token 时返回。注意事项:建议在到期前调用刷新 user_access_token 接口获取新的 refresh_token。 |
| token_type | string | 值固定为 Bearer,仅在请求成功时返回 |
| scope | string | 本次请求所获得的 access_token 所具备的权限列表,以空格分隔,仅在请求成功时返回 |
| error | string | 错误类型,仅在请求失败时返回 |
| error_description | string | 具体的错误信息,仅在请求失败时返回 |
响应体示例
成功响应示例:
{
"code": 0,
"access_token": "eyJhbGciOiJFUzI1NiIs**********X6wrZHYKDxJkWwhdkrYg",
"expires_in": 7200, // 非固定值,请务必根据响应体中返回的实际值来确定 access_token 的有效期
"refresh_token": "eyJhbGciOiJFUzI1NiIs**********XXOYOZz1mfgIYHwM8ZJA",
"refresh_token_expires_in": 604800, // 非固定值,请务必根据响应体中返回的实际值来确定 refresh_token 的有效期
"scope": "auth:user.id:read offline_access task:task:read user_profile",
"token_type": "Bearer"
}
失败响应示例:
{
"code": 20050,
"error": "server_error",
"error_description": "An unexpected server error occurred. Please retry your request."
}
错误码
| HTTP 状态码 | 错误码 | 描述 | 排查建议 |
|---|---|---|---|
| 400 | 20001 | The request is missing a required parameter. | 必要参数缺失,请检查请求时传入的参数是否有误 |
| 400 | 20002 | The client secret is invalid. | 应用认证失败,请检查提供的 client_id 与 client_secret 是否正确 |
| 400 | 20003 | The authorization code is not found. Please note that an authorization code can only be used once. | 无效的授权码,请检查授权码是否有效,注意授权码仅能使用一次 |
| 400 | 20004 | The authorization code has expired. | 授权码已经过期,请在授权码生成后的 5 分钟内使用 |
| 400 | 20008 | The user does not exist. | 用户不存在,请检查发起授权的用户的当前状态 |
| 400 | 20009 | The specified app is not installed. | 租户未安装应用,请检查应用状态 |
| 400 | 20010 | The user does not have permission to use this app. | 用户无应用使用权限,请检查发起授权的用户是否仍具有应用使用权限 |
| 400 | 20024 | The provided authorization code or refresh token does not match the provided client ID. | 提供的授权码与 client_id 不匹配,请勿混用不同应用的凭证 |
| 400 | 20036 | The specified grant_type is not supported. | 无效的 grant_type,请检查请求体中 grant_type 字段的取值 |
| 400 | 20048 | The specified app does not exist. | 应用不存在,请检查应用状态 |
| 400 | 20049 | PKCE code challenge failed. | PKCE 校验失败,请检查请求体中 code_verifier 字段是否存在且有效 |
| 500 | 20050 | An unexpected server error occurred. Please retry your request. | 内部服务错误,请稍后重试,如果持续报错请联系技术支持 |
| 400 | 20063 | The request is malformed. Please check your request. | 请求体中缺少必要字段,请根据具体的错误信息补齐字段 |
| 400 | 20065 | The authorization code has been used. Please note that an authorization code can only be used once. | 授权码已被使用,授权码仅能使用一次,请检查是否有被重复使用 |
| 400 | 20066 | The user status is invalid. | 用户状态非法,请检查发起授权的用户的当前状态 |
| 400 | 20067 | The provided scope list contains duplicate scopes. Please ensure all scopes are unique. | 无效的 scope 列表,其中存在重复项,请确保传入的 scope 列表中没有重复项 |
| 400 | 20068 | The provided scope list contains scopes that are not permitted. Please ensure all scopes are allowed. | 无效的 scope 列表,其中存在用户未授权的权限。当前接口 scope 参数传入的权限必须是获取授权码时 scope 参数值的子集。例如,在获取授权码时,用户授权了权限 A、B,则当前接口 scope 可传入的值只有权限 A、B,若传入权限 C 则会返回当前错误码。 |
| 400 | 20069 | The specified app is not enabled. | 应用未启用,请检查应用状态 |
| 400 | 20070 | Multiple authentication methods were provided. Please only use one to proceed. | 请求时同时使用了 Basic Authentication 和 client_secret 两种身份验证方式。请仅使用 client_id、client_secret 身份验证方式调用本接口。 |
| 400 | 20071 | The provided redirect URI does not match the one used during authorization. | 无效的 redirect_uri,请确保 redirect_uri 与获取授权码时传入的 redirect_uri 保持一致 |
| 503 | 20072 | The server is temporarily unavailable. Please retry your request. | 服务暂不可用,请稍后重试 |
代码示例
注意事项:此处提供的代码示例仅供参考,请勿直接在生产环境使用
Golang
运行下面示例程序的步骤:
- 点击下方代码块右上角复制按钮,将代码复制到本地文件中,保存为
main.go; - 参照注释部分,完成配置;
- 在
main.go所在目录下新建.env文件,内容如下:APP_ID=cli_xxxxxx # 仅为示例值,请使用你的应用的 App ID,获取方式:开发者后台 -> 基础信息 -> 凭证与基础信息 -> 应用凭证 -> App ID APP_SECRET=xxxxxx # 仅为示例值,请使用你的应用的 App Secret,获取方式:开发者后台 -> 基础信息 -> 凭证与基础信息 -> 应用凭证 -> App Secret - 在
main.go所在目录执行以下命令:go mod init oauth-test go get github.com/gin-gonic/gin go get github.com/gin-contrib/sessions go get github.com/gin-contrib/sessions/cookie go get github.com/joho/godotenv go get golang.org/x/oauth2 go run main.go - 浏览器打开 http://localhost:8080 ,按照页面提示完成授权流程;
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"math/rand"
"net/http"
"os"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
_ "github.com/joho/godotenv/autoload"
"golang.org/x/oauth2"
)
var oauthEndpoint = oauth2.Endpoint{
AuthURL: "https://accounts.feishu.cn/open-apis/authen/v1/authorize",
TokenURL: "https://open.feishu.cn/open-apis/authen/v2/oauth/token",
}
var oauthConfig = &oauth2.Config{
ClientID: os.Getenv("APP_ID"),
ClientSecret: os.Getenv("APP_SECRET"),
RedirectURL: "http://localhost:8080/callback", // 请先添加该重定向 URL,配置路径:开发者后台 -> 开发配置 -> 安全设置 -> 重定向 URL -> 添加
Endpoint: oauthEndpoint,
Scopes: []string{"offline_access"}, // 如果你不需要 refresh_token,请注释掉该行,否则你需要先申请 offline_access 权限方可使用,配置路径:开发者后台 -> 开发配置 -> 权限管理
}
func main() {
r := gin.Default()
// 使用 Cookie 存储 session
store := cookie.NewStore([]byte("secret")) // 此处仅为示例,务必不要硬编码密钥
r.Use(sessions.Sessions("mysession", store))
r.GET("/", indexController)
r.GET("/login", loginController)
r.GET("/callback", oauthCallbackController)
fmt.Println("Server running on http://localhost:8080")
log.Fatal(r.Run(":8080"))
}
func indexController(c *gin.Context) {
c.Header("Content-Type", "text/html; charset=utf-8")
var username string
session := sessions.Default(c)
if session.Get("user") != nil {
username = session.Get("user").(string)
}
html := fmt.Sprintf(`<html><head><style>body{font-family:Arial,sans-serif;background:#f4f4f4;margin:0;display:flex;justify-content:center;align-items:center;height:100vh}.container{text-align:center;background:#fff;padding:30px;border-radius:10px;box-shadow:0 0 10px rgba(0,0,0,0.1)}a{padding:10px 20px;font-size:16px;color:#fff;background:#007bff;border-radius:5px;text-decoration:none;transition:0.3s}a:hover{background:#0056b3}}</style></head><body>[返回主页](/)
</body></html>`, user.Data.Name)
c.String(http.StatusOK, html)
}
刷新 user_access_token
OAuth 令牌接口,可用于刷新 user_access_token 以及获取新的 refresh_token。
-
user_access_token为用户访问凭证,使用该凭证可以以用户身份调用 OpenAPI,该凭证存在有效期,可通过refresh_token进行刷新。 -
用户授权时,用户必须拥有应用的使用权限,否则调用本接口将会报错误码 20010。
-
refresh_token用于获取新的user_access_token,且仅能使用一次。在获取新的user_access_token时会返回新的refresh_token,原refresh_token立即失效。 -
首次获取
refresh_token的方式参见获取 user_access_token。 注意事项:本接口实现遵循 RFC 6749 - The OAuth 2.0 Authorization Framework ,你可以使用标准的 OAuth 客户端库进行接入(推荐)
前置工作
开通 offline_access 权限
获取 refresh_token 需前往开放平台应用后台的权限管理模块开通 offline_access 权限,并在发起授权时在 scope 参数中声明该权限。
在开通 offline_access 权限后,如需获取 refresh_token,具体的请求参数设置如下:
- 首先在发起授权时,授权链接的
scope参数中必须拼接offline_access,例如:
https://accounts.feishu.cn/open-apis/authen/v1/authorize?client_id=cli_a5d611352af9d00b&redirect_uri=https%3A%2F%2Fexample.com%2Fapi%2Foauth%2Fcallback&scope=bitable:app:readonly%20offline_access
- 在获取 user_access_token时,
- 如果不需要缩减权限,即该接口的
scope参数为空,则无需做其他操作,即可正常获得refresh_token; - 如果需要缩减权限,即该接口的
scope参数不为空,- 且需要获取
refresh_token,则此处的scope参数中需要拼接offline_access; - 如不需要获取
refresh_token,则无需特殊处理;
- 且需要获取
- 如果不需要缩减权限,即该接口的
- 在刷新 user_access_token时,同第二步的逻辑。
开启刷新 user_access_token 的安全设置
注意事项:- 如果你看不到此开关则无需关注,其默认处于开启状态。
前往开放平台应用后台的安全设置模块,打开刷新 user_access_token 的开关。
请求
注意事项:为了避免刷新 user_access_token 的行为被滥用,在用户授权应用 365 天后,应用必须通过用户重新授权的方式来获取 user_access_token 与 refresh_token。如果 refresh_token 到期后继续刷新user_access_token将报错(错误码为 20037),可参考以下错误码描述信息进行处理。
注意事项:刷新后请更新本地 user_access_token 和 refresh_token,原令牌将无法再使用(user_access_token 会有一分钟的豁免时间以供应用完成令牌轮转)。
| 基本 | |
|---|---|
| HTTP URL | https://open.feishu.cn/open-apis/authen/v2/oauth/token |
| HTTP Method | POST |
| 接口频率限制 | 1000 次/分钟、50 次/秒 |
| 支持的应用类型 | Custom App、Store App |
| 权限要求 调用该 API 所需的权限。开启其中任意一项权限即可调用 |
无 |
| 字段权限要求 | refresh_token 以及 refresh_token_expires_in 字段仅在具备以下权限时返回:offline_access(offline_access) |
请求头
| 名称 | 类型 | 必填 | 描述 |
|---|---|---|---|
| Content-Type | string | 是 | 请求体类型。 固定值: application/json; charset=utf-8 |
请求体
| 名称 | 类型 | 必填 | 描述 |
|---|---|---|---|
| grant_type | string | 是 | 授权类型。 固定值: refresh_token |
| client_id | string | 是 | 应用的 App ID,可以在开发者后台中的应用详情页面找到该值。 示例值: cli_a5ca35a685b0x26e |
| client_secret | string | 是 | 应用的 App Secret,可以在开发者后台中的应用详情页面找到该值,详见:如何获取应用的 App ID。 示例值: baBqE5um9LbFGDy3X7LcfxQX1sqpXlwy |
| refresh_token | string | 是 | 刷新令牌,用于刷新 user_access_token 以及 refresh_token。注意事项:请务必注意本接口仅支持获取 user_access_token和刷新 user_access_token接口返回的 refresh_token示例值: eyJhbGciOiJFUzI1NiIs**********XXOYOZz1mfgIYHwM8ZJA |
| scope | string | 否 | 该参数用于缩减 user_access_token 的权限范围。例如: 1. 在获取授权码时通过 scope 参数授权了 contact:user.base:readonly contact:contact.base:readonly contact:user.employee:readonly 三个权限。2. 在当前接口可通过 scope 参数传入 contact:user.base:readonly,将 user_access_token 的权限缩减为 contact:user.base:readonly 这一个。注意: - 如果不指定当前参数,生成的 user_access_token 将包含用户授权时的所有权限。- 当前参数不能传入重复的权限,否则会接口调用会报错,返回错误码 20067。 - 当前参数不能传入未授权的权限(即获取授权码时用户已授权范围外的其他权限),否则接口调用会报错,返回错误码 20068。 - 多次调用当前接口缩减权限的范围不会叠加。例如,用户授予了权限 A 和 B,第一次调用该接口缩减为权限 A,则 user_access_token 只包含权限 A;第二次调用该接口缩减为权限 B,则 user_access_token 只包含权限 B。 - 生效的权限列表可通过本接口返回值 scope 查看。 格式要求: 以空格分隔的 scope 列表示例值: auth:user.id:read task:task:read |
请求体示例
{
"grant_type": "refresh_token",
"client_id": "cli_a5ca35a685b0x26e",
"client_secret": "baBqE5um9LbFGDy3X7LcfxQX1sqpXlwy",
"refresh_token": "eyJhbGciOiJFUzI1NiIs**********XXOYOZz1mfgIYHwM8ZJA"
}
响应
响应体类型为 application/json; charset=utf-8。
响应体
| 名称 | 类型 | 描述 |
|---|---|---|
| code | int | 错误码,为 0 时表明请求成功,非 0 表示失败,请参照下文错误码一节妥善处理 |
| access_token | string | 即 user_access_token,仅在请求成功时返回 |
| expires_in | int | 即 user_access_token 的有效期,单位为秒,仅在请求成功时返回注意事项:建议使用该字段以确定 user_access_token 的过期时间,不要硬编码有效期 |
| refresh_token | string | 用于刷新 user_access_token,该字段仅在请求成功且用户授予 offline_access 权限时返回:offline_access(offline_access) 注意事项:如果你在获取 user_access_token 时设置了 scope 请求参数,且需要返回 refresh_token,则需要在 scope 参数中包括 offline_access。另外,refresh_token 仅能被使用一次。 |
| refresh_token_expires_in | int | 即 refresh_token 的有效期,单位为秒,仅在返回 refresh_token 时返回。注意事项:建议在到期前重新调用当前接口获取新的 refresh_token。 |
| token_type | string | 值固定为 Bearer,仅在请求成功时返回 |
| scope | string | 本次请求所获得的 access_token 所具备的权限列表,以空格分隔,仅在请求成功时返回 |
| error | string | 错误类型,仅在请求失败时返回 |
| error_description | string | 具体的错误信息,仅在请求失败时返回 |
响应体示例
成功响应示例:
{
"code": 0,
"access_token": "eyJhbGciOiJFUzI1NiIs**********X6wrZHYKDxJkWwhdkrYg",
"expires_in": 7200, // 非固定值,请务必根据响应体中返回的实际值来确定 access_token 的有效期
"refresh_token": "eyJhbGciOiJFUzI1NiIs**********VXOYOZYZmfgIYHWM0ZJA",
"refresh_token_expires_in": 604800, // 非固定值,请务必根据响应体中返回的实际值来确定 refresh_token 的有效期
"scope": "auth:user.id:read offline_access task:task:read user_profile",
"token_type": "Bearer"
}
失败响应示例:
{
"code": 20050,
"error": "server_error",
"error_description": "An unexpected server error occurred. Please retry your request."
}
错误码
| HTTP 状态码 | 错误码 | 描述 | 排查建议 |
|---|---|---|---|
| 400 | 20001 | The request is missing a required parameter. | 必要参数缺失,请检查请求时传入的参数是否有误 |
| 400 | 20002 | The client secret is invalid. | 应用认证失败,请检查提供的 client_id 与 client_secret 是否正确。获取方式参见 如何获取应用的 App ID。 |
| 400 | 20008 | The user does not exist. | 用户不存在,请检查发起授权的用户的当前状态 |
| 400 | 20009 | The specified app is not installed. | 租户未安装应用,请检查应用状态 |
| 400 | 20010 | The user does not have permission to use this app. | 用户无应用使用权限,请检查发起授权的用户是否仍具有应用使用权限 |
| 400 | 20024 | The provided authorization code or refresh token does not match the provided client ID. | 提供的 refresh_token 与 client_id 不匹配,请勿混用不同应用的凭证 |
| 400 | 20026 | The refresh token passed is invalid. Please check the value. | 请检查请求体中 refresh_token 字段的取值请注意本接口仅支持 v2 版本接口下发的 refresh_token |
| 400 | 20036 | The specified grant_type is not supported. | 无效的 grant_type,请检查请求体中 grant_type 字段的取值 |
| 400 | 20037 | The refresh token passed has expired. Please generate a new one. | refresh_token 已过期,请重新发起授权流程以获取新的 refresh_token |
| 400 | 20048 | The specified app does not exist. | 应用不存在,请检查应用状态 |
| 500 | 20050 | An unexpected server error occurred. Please retry your request. | 内部服务错误,请稍后重试,如果持续报错请联系技术支持 |
| 400 | 20063 | The request is malformed. Please check your request. | 请求体中缺少必要字段,请根据具体的错误信息补齐字段 |
| 400 | 20064 | The refresh token has been revoked. Please note that a refresh token can only be used once. | refresh_token 已被撤销,请重新发起授权流程以获取新的 refresh_token |
| 400 | 20066 | The user status is invalid. | 用户状态非法,请检查发起授权的用户的当前状态 |
| 400 | 20067 | The provided scope list contains duplicate scopes. Please ensure all scopes are unique. | 无效的 scope 列表,其中存在重复项,请确保传入的 scope 列表中没有重复项 |
| 400 | 20068 | The provided scope list contains scopes that are not permitted. Please ensure all scopes are allowed. | 无效的 scope 列表,其中存在用户未授权的权限。当前接口 scope 参数传入的权限必须是获取授权码时 scope 参数值的子集。例如,在获取授权码时,用户授权了权限 A、B,则当前接口 scope 可传入的值只有权限 A、B,若传入权限 C 则会返回当前错误码。 |
| 400 | 20069 | The specified app is not enabled. | 应用未启用,请检查应用状态 |
| 400 | 20070 | Multiple authentication methods were provided. Please only use one to proceed. | 请求时同时使用了 Basic Authentication 和 client_secret 两种身份验证方式。请仅使用 client_id、client_secret 身份验证方式调用本接口。 |
| 503 | 20072 | The server is temporarily unavailable. Please retry your request. | 服务暂不可用,请稍后重试 |
| 400 | 20073 | The refresh token has been used. Please note that a refresh token can only be used once. | 请重新发起授权流程以获取新的 refresh_token |
| 400 | 20074 | The specified app is not allowed to refresh token. | 请在应用管理后台检查是否开启了刷新 user_access_token 开关,注意发版后生效 |


