用户授权 OAuth2
阅读指引:本篇是《开放平台服务端 API 指南》的第 4 部分。接口基础地址、通用约定与各接口请求限制,请先阅读《服务端 API 概览》。
3. 应用权限与用户授权
管理员授予的是应用能力上限;用户 OAuth2 授权是该上限内当前用户同意的范围;应用发布范围决定用户使用应用及用户、组织接口的资源边界;群接口另外以应用与目标群的有效绑定为授权依据。三者不能互相替代。
| Scope | 类型 | 入口 | 状态 |
|---|---|---|---|
user:profile:read |
需要用户授权 | GET /openapi/v1/user/profile |
可用 |
material:manage |
应用能力,默认授权 | POST /openapi/v1/materials<br>GET /openapi/v1/materials/content |
可用 |
contact:organization:read |
应用能力,需管理员授权 | GET/POST /openapi/v1/organization/* |
可用 |
chat:conversation:choose |
应用能力,默认授权 | JSAPI chooseChat,需要 config |
可用 |
chat:conversation:open |
应用能力,需管理员授权 | JSAPI openChat,需要 config |
可用 |
group:app |
应用能力,需管理员授权并由群主/管理员添加到群 | GET /openapi/v1/groups/*;POST /openapi/v1/messages/send,recipient.type=group |
可用 |
message:send:user |
应用能力,默认授权 | POST /openapi/v1/messages/send,recipient.type=user |
可用 |
message:send:org_node |
应用能力,需管理员授权 | POST /openapi/v1/messages/send,recipient.type=org_node |
可用 |
message:send:all |
应用能力,需管理员授权 | POST /openapi/v1/messages/send,recipient.type=all |
可用 |
message:delegate:send |
需要用户授权 | POST /openapi/v1/messages/delegate_send,recipient.type=user/group |
可用 |
- 应用只能取得管理员已授予 Scope 的 Token。
- 用户 OAuth2 只能申请
requires_user_auth=true的 Scope。 - 权限变更后既有 access token 和 refresh token 被撤销,应用必须重新认证。
- 应用不能根据 Scope 名称猜测接口;以本文公开入口及应用实际获授能力为准。
- 用户和组织接口受第 12 章发布范围约束。群资料和群成员读取以
group:app与有效群绑定为依据,返回绑定群的成员,不按用户或组织发布范围逐个裁剪。
5. 普通 H5 OAuth2 模式
适用于在平台兼容客户端中打开的 H5,直接导航 OAuth2 authorize URL、不使用 JSSDK getAuthCode 的场景。H5 只需构造授权地址,当前登录用户由客户端提供。普通外部浏览器仅打开此地址不能建立平台用户身份,也不能完成授权;不要把它用作独立网页登录入口。
H5 导航 /oauth2/authorize
→ 平台验证当前登录用户、应用状态、发布范围、回调地址和 Scope
→ 302 跳转到已登记的 redirect_uri,携带 code/state
→ 应用后端校验 state,并用 app_key + app_secret 换用户 Token
GET /openapi/v1/oauth2/authorize
https://openapi.example.com/openapi/v1/oauth2/authorize
?client_id=svc_app_xxx
&redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback
&response_type=code
&scope=user%3Aprofile%3Aread
&state=csrf_random_value
| query 参数 | 类型 | 必填 | 规则 |
|---|---|---|---|
client_id |
string | 是 | 固定为应用 app_key;首尾空白会被去除。 |
redirect_uri |
string | 是 | 必须与管理员登记的完整回调 URI 精确一致,包括协议、主机、端口、路径和 query;不支持通配符或 fragment。 |
response_type |
string | 是 | 固定为 code。 |
scope |
string | 是 | 一个或多个需要用户授权、且已由管理员授予应用的 Scope;多个值以单个空格分隔。 |
state |
string | 是 | 应用生成的非空 UTF-8 高熵随机值,最多 256 字节;平台原样返回,应用必须校验以防止 CSRF 和请求串线。 |
静默授权从兼容的已登录客户端进入并符合授权条件时,授权可能直接完成并返回 code,不一定展示确认页。应用必须按回调结果处理,不能假设每次都弹出授权确认,也不能自行模拟用户身份。
发布范围会在授权链路中重复检查authorize、authorization code 换 Token、用户 Token 刷新以及后续用户接口都会按当前范围复核。用户被移出范围后,应用应结束该用户会话,不要继续刷新旧 Token。
| authorize 结果 | HTTP 行为 | 字段 |
|---|---|---|
| 成功 | 302 Found 跳转到已登记的 redirect_uri |
code:短期、一次性 authorization code;state:请求值原样返回。 |
| 可安全回调的失败 | 302 Found 跳转到已验证属于该应用的 redirect_uri |
error、error_description 和原始 state。 |
| 无法信任回调地址的失败 | HTTP 400 或 500 JSON | error 与可选 error_description;不会跳转到未验证地址。 |
用 code 换用户 Token
curl -X POST "${OPENAPI_BASE_URL}/openapi/v1/oauth2/token" \
-u "${APP_KEY}:${APP_SECRET}" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code=${AUTHORIZATION_CODE}" \
--data-urlencode "redirect_uri=https://app.example.com/oauth/callback"
| 请求字段 | 必填 | 类型与规则 |
|---|---|---|
grant_type |
是 | string;固定为 authorization_code。 |
code |
是 | string;authorize 回调或 JSSDK getAuthCode 返回的短期一次性 code。 |
redirect_uri |
是 | string;必须与申请 code 时使用的值完全一致。 |
| 客户端凭据 | 是 | 该 code 所属应用的 app_key + app_secret,推荐使用 HTTP Basic。 |
{
"access_token": "atk_mim_user_xxx",
"refresh_token": "rtk_mim_user_xxx",
"expires_in": 7200,
"refresh_expires_in": 2592000,
"token_type": "Bearer",
"scope": "user:profile:read",
"client_id": "svc_app_xxx",
"user_open_id": "10001"
}
| 响应字段 | JSON 类型 | 含义与单位 |
|---|---|---|
access_token |
string | 代表当前应用和当前授权用户的用户访问令牌。 |
refresh_token |
string | 用户级刷新令牌;只能由应用后端保存。 |
expires_in |
integer | access token 剩余有效时间,单位秒。 |
refresh_expires_in |
integer | refresh token 剩余有效时间,单位秒。 |
token_type |
string | 固定为 Bearer。 |
scope |
string | 用户实际授权的 Scope,以单个空格分隔。 |
client_id |
string | 应用的 app_key。 |
user_open_id |
string | 当前应用可使用的用户标识;按不透明字符串保存和比较。 |
刷新用户 Token
仍调用同一 Token endpoint。服务端从 refresh token 的可信上下文识别它是用户 Token,不能通过请求字段把应用 Token 改成用户 Token。
curl -X POST "${OPENAPI_BASE_URL}/openapi/v1/oauth2/token" \
-u "${APP_KEY}:${APP_SECRET}" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=refresh_token" \
--data-urlencode "refresh_token=${USER_REFRESH_TOKEN}"
撤销当前用户授权
POST /openapi/v1/oauth2/user_authorization/revoke
curl -X POST "${OPENAPI_BASE_URL}/openapi/v1/oauth2/user_authorization/revoke" \
-H "Authorization: Bearer ${USER_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"scope":"user:profile:read"}'
| 请求字段 | 必填 | 类型与规则 |
|---|---|---|
scope |
否 | string;传入时撤销当前用户的这一个 Scope,空字符串或省略时撤销当前用户在该应用下的全部授权。 |
请求不传用户 ID。平台只操作 Bearer Token 对应的当前应用与当前用户,并使相关用户 access/refresh token 失效。
{
"code": 200,
"message": "ok",
"data": {
"app_id": 4611686018427397904,
"user_open_id": "10001",
"revoked_rows": 1
}
}
| 响应字段 | JSON 类型 | 含义 |
|---|---|---|
code |
integer | 业务码;成功为 200。 |
message |
string | 简短结果文本;业务判断以 code 为准。 |
data.app_id |
integer | 保留字段;应用不应持久化或依赖该字段。 |
data.user_open_id |
string | 被撤销授权的当前用户标识。 |
data.revoked_rows |
integer | 实际撤销的授权记录数量,无单位;没有匹配的有效授权记录时可能为 0。成功撤销后当前 Token 已失效,不应使用它再次调用撤销接口。 |
6. JSSDK OAuth2 模式
getAuthCode 用于在 H5 内获取当前登录用户的 authorization code。该 JSAPI 已开放,必须在 ready 后调用,但不要求先执行 config。H5 只需提交文档列出的应用、回调地址、Scope 和 state 参数。
const result = await im.openPlatform.getAuthCode({
appKey: "svc_app_xxx",
redirectUri: "https://app.example.com/oauth/callback",
scopes: ["user:profile:read"],
state: crypto.randomUUID()
})
// result.authorizationCode / result.state / result.expiresIn
// 将 code 交给应用后端,后端仍使用 app_key + app_secret 换 Token。
| 请求字段 | JS 类型 | 必填 | 规则 |
|---|---|---|---|
appKey |
string | 是 | 当前应用的 app_key;它是公开选择器,不是身份凭据。 |
redirectUri |
string | 是 | 必须与管理员登记的完整 OAuth2 回调 URI 精确一致。 |
scopes |
string[] | 是 | 非空数组;每项是一个不含空白的用户授权 Scope,必须已授予当前应用且要求用户授权。 |
state |
string | 是 | 非空 UTF-8 字符串,最多 256 字节;应用生成并在换码链路中校验。 |
| 成功结果字段 | JS 类型 | 含义与单位 |
|---|---|---|
authorizationCode |
string | 短期、一次性 OAuth2 code,只应交给应用后端换用户 Token。 |
state |
string | 请求值原样返回;应用必须与发起请求时保存的值比较。 |
expiresIn |
number | authorization code 剩余有效时间,单位秒。 |
H5 getAuthCode
→ 平台验证当前登录用户、应用发布范围、应用页面、回调地址和 Scope
→ 成功后返回短期、一次性的 code
→ 应用后端使用 code 换取用户 Token
当前用户和运行环境由平台识别,H5 只提交上述已文档化参数,不要附加用户身份、登录凭据或运行环境参数。调用失败时 Promise reject,错误对象使用第 11.6 节定义的统一 code/message/requestId/details 结构。
7. 获取当前授权用户信息与头像
GET /openapi/v1/user/profile
要求用户态 Bearer Token,并同时具备应用 Scope 和用户授权 Scope user:profile:read;当前用户还必须仍在应用发布范围内。
| 请求项 | 必填 | 规则 |
|---|---|---|
Authorization Header |
是 | Bearer {USER_ACCESS_TOKEN};不接受应用级 Token。 |
| query/body | 否 | 本接口没有 query 参数和请求 body;用户身份只来自 Token。 |
curl "${OPENAPI_BASE_URL}/openapi/v1/user/profile" \
-H "Authorization: Bearer ${USER_ACCESS_TOKEN}"
{
"code": 200,
"message": "ok",
"data": {
"user_open_id": "10001",
"name": "张三",
"avatar": {
"thumbnail_url": "https://openapi.example.com/openapi/v1/media/avatar/thumbnail?resource=opaque_xxx",
"original_url": "https://openapi.example.com/openapi/v1/media/avatar/original?resource=opaque_yyy",
"expires_at": 1787400300
}
}
}
| 响应字段 | JSON 类型 | 含义与单位 |
|---|---|---|
code |
integer | 业务码;成功为 200。 |
message |
string | 简短结果文本;业务判断以 code 为准。 |
data.user_open_id |
string | 当前应用下的用户标识,按不透明字符串处理。 |
data.name |
string | 用户当前展示名称,可能随用户资料变化。 |
data.avatar.thumbnail_url |
string | 短期缩略头像 URL。 |
data.avatar.original_url |
string | 短期原始头像 URL。 |
data.avatar.expires_at |
integer | 两条头像 URL 的失效时间,Unix 秒时间戳。 |
下载头像
GET /openapi/v1/media/avatar/{thumbnail|original}?resource={opaque}
curl -L "${THUMBNAIL_URL}" -o avatar-thumbnail.jpg
| 下载项 | 规则 |
|---|---|
| 路径参数 | thumbnail 下载缩略图;original 下载原图。 |
resource |
必填且只能出现一次;完整使用 profile 返回的不透明值,不得解析、修改或重复编码。 |
| 成功响应 | HTTP 200,body 为图片二进制;Content-Type 由实际头像文件决定,不使用 JSON 包装。 |
| 失败响应 | 篡改、过期、规格错误、文件不存在或回源失败统一为 HTTP 404 空 body。 |
- 只使用响应中的下载 URL 获取头像,不要推断或拼接其他文件地址;URL 绑定应用、用户、文件规格与到期时间。
- 默认有效期 5 分钟,部署上限 15 分钟;过期后重新请求 profile。
- 下载 URL 不需要再次提交 Token。篡改、过期、文件不存在或回源失败统一返回 HTTP 404 空响应。
- 应用只能查询 Token 对应的当前用户,接口不接收
user_id/user_open_id。