# 浏览器网页接入指南

## 整体流程

![授权流程概述.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/2f2a8b7d7c56087cc2d50c9df7c91932_LW5kDPce0i.png?height=1466&lazyload=true&maxWidth=700&width=2092)

## 操作步骤

### 第一步：配置重定向地址
在开发者后台创建应用或选择已有应用，进入应用详情页 > 开发配置 > 安全设置模块，配置重定向 URL。

![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/1dc62df21b034736fa83dfb82fcc710f_uPyfgwoEUj.png?height=687&lazyload=true&maxWidth=600&width=1756)

### 第二步：申请所需权限

根据开发者实际调用 API 所需的权限，在权限管理模块内进行申请开通，并发布应用使配置生效。

关于申请权限的介绍可见：[申请 API 权限](https://open.feishu.cn/document/ukTMukTMukTM/uQjN3QjL0YzN04CN2cDN)，应用发布流程可参考文档[自建应用发布流程](https://open.feishu.cn/document/home/introduction-to-custom-app-development/self-built-application-development-process)和[商店应用发布流程](https://open.feishu.cn/document/uMzNwEjLzcDMx4yM3ATM/uYjMyUjL2IjM14iNyITN)。

![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6ed3d748ee4258f066a55fd29c34534e_BhnIeqwAWO.png?height=873&lazyload=true&maxWidth=600&width=1886)

### 第三步：构造授权链接

通过[飞书授权页](https://open.feishu.cn/document/common-capabilities/sso/api/obtain-oauth-code)的 scope 参数传入需要用户授权的权限 key，以空格隔开。相应权限需要在步骤二中已申请开通。如果未开通，页面将返回当前应用未申请权限的报错从而导致用户无法完成授权。

假设你的应用 ID 为 `cli_a5ca35a685b0x26e`，重定向地址为 `https://example.com/api/oauth/callback`，`state` 参数设置为`123456`，需要授权的 scope 有 `admin:app.admin_id:readonly`以及 `calendar:calendar`，则授权链接如下：warning
- 代码中的换行和注释只是为了方便解释，实际不需要。
- 代码中的参数值仅为示例值，请替换为实际值。

```bash
https://accounts.feishu.cn/open-apis/authen/v1/authorize
?client_id=cli_a5ca35a685b0x26e # 应用的唯一ID，在开发者后台【凭证和基础信息】中可以获得
&redirect_uri=https%3A%2F%2Fexample.com%2Fapi%2Foauth%2Fcallback # 授权完成后的重定向地址，需要进行 URL 编码
&state=123456 # 用来维护请求和回调状态的附加字符串，在授权完成回调时会附加此参数，应用可以根据此字符串来判断上下文关系
&scope=admin:app.admin_id:readonly%20calendar:calendar # 为由空格分隔的需要用户授权的三方应用权限，权限点位需要在步骤一中已申请开通，注意一样需要进行 URL 编码（空格会被自动替换为 %20）
```

### 第四步：请求用户授权

在用户浏览器端打开授权链接，用户在飞书授权页面将看到应用请求增量授权的权限信息，历史已授权给应用的权限信息将不再展示。

![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/f81ae3c268c663ec9c5ee9a73c58f91c_Hu8RN47Nq9.png?height=1012&lazyload=true&maxWidth=400&width=1076)

### 第五步：用户同意授权，获取授权码

用户同意授权后，授权页将跳转到通过 redirect_uri 参数传入的重定向 URL，并携带飞书开放平台返回的授权码 code 和之前授权时传入的 state 参数。

例如：

```
https://example.com/api/oauth/callback?code=1yxlUSU8Rf+7B32HY3HR7g&state=123456
```

如果用户拒绝授权，授权页将跳转到重定向 URL，并携带 'access_denied' 的错误码，以及之前授权时传入的 state 参数，建议应用开发者对拒绝授权的场景进行相应的流程设计，确保用户在拒绝授权的场景下应用的使用体验。

例如：

```
https://example.com/api/oauth/callback?error=access_denied&state=123456
```

### 第六步：通过授权码，获取 user_access_token

在应用获取到授权码 code 后，应用通过调用[获取 user_access_token 的接口](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/authentication-management/access-token/get-user-access-token)来获取 user_access_token 并进行后续服务端 Open API 的调用。

请求 URL：`https://open.feishu.cn/open-apis/authen/v2/oauth/token`

请求方法：`POST`

请求体（JSON 格式）：warning
代码中的参数值仅为示例值，请替换为实际值。

```json
{
    "grant_type": "authorization_code",
    "client_id": "cli_a5ca35a685b0x26e",
    "client_secret": "baBqE5um9LbFGDy3X7LcfxQX1sqpXlwy",
    "code": "a61hb967bd094dge949h79bbexd16dfe",
    "redirect_uri": "https://example.com/api/oauth/callback"
}
```

响应体（JSON 格式）：
```json
{
    "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"
}
```

### 第七步：通过 user_access_token，调用 Open API

使用 user_access_token 调用服务端 Open API 接口时，若接口所需权限点位未经授权，接口会返回报错，您需要重新发起授权流程来获取用户相应的授权。

缺少授权时，调用服务端接口的报错示例：

```json
{
    "code": 99991679,
    "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: [docx:document, docx:document:readonly]",
    "error": {
        "log_id": "202308071603455425F5854702B4000DB9",
        "permission_violations": [
            {
                "type": "action_privilege_required",
                "subject": "docx:document"
            },
            {
                "type": "action_privilege_required",
                "subject": "docx:document:readonly"
            }
        ]
    }
}
```

### 第八步：刷新 user_access_token（可选）

在 refresh token 有效期内，如有必要，您可以使用 refresh token 刷新生成一个新的 user access token]，更多细节请参考[刷新 user_access_token](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/authentication-management/access-token/refresh-user-access-token)。

请求 URL：`https://open.feishu.cn/open-apis/authen/v2/oauth/token`

请求方法：`POST`

请求体（JSON 格式）：warning
代码中的参数值仅为示例值，请替换为实际值。

```json
{
    "grant_type": "refresh_token",
    "client_id": "cli_a5ca35a685b0x26e",
    "client_secret": "baBqE5um9LbFGDy3X7LcfxQX1sqpXlwy",
    "refresh_token": "eyJhbGciOiJFUzI1NiIs**********XXOYOZz1mfgIYHwM8ZJA"
}
```

响应体（JSON 格式）：
```json
{
    "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"
}
```

