# 微信网页授权

如果用户在微信客户端中访问第三方网页，服务号可以通过微信网页授权机制，来获取用户基本信息，进而实现业务逻辑。

微信网页授权能力调整公告

根据相关法律法规，为进一步规范开发者调用用户信息相关接口或功能，保障用户合法权益，服务号用户信息获取接口将不再返回用户性别及地区信息，能力调整已于 2021 年 10 月 20 日 24 时生效。详情点击查看原公告[《微信公众平台用户信息相关接口调整公告》](https://developers.weixin.qq.com/community/develop/doc/00028edbe3c58081e7cc834705b801?blockType=1)
为进一步规范能力使用，保障用户合法权益，平台将对微信网页授权能力进行调整。**当开发者在网页中在不规范使用发起 snsapi_userinfo 网页授权时，微信将默认打开网页快照页模式进行基础浏览。**能力调整已于 2022 年 7 月 12 日 24 时生效。详情点击查看原公告[《微信网页授权能力调整公告》](https://developers.weixin.qq.com/community/minihome/doc/000c2c34068880629ced91a2f56001)

## 一、开发前必读

网页授权流程分为四步：

1. 引导用户进入授权页面
2. 接受用户授权同意，在回调页面中获取code
3. 通过code换取用户授权access_token（与服务端接口 access_token 不同）
4. 通过用户授权 access_token 和 openid 获取用户基本信息（支持UnionID机制）

在完成授权流程之前，需要认真阅读下面的信息，掌握基本的背景知识。

##### 1. 适用账号

网页授权支持已认证的服务号使用，其他类型的账号（部分公众号/小程序/网站应用/移动应用等除了已认证服务号之外的账号）均不支持使用微信网页授权登录功能。

**特殊补充：** 已认证的政府、事业单位和媒体类型的公众号也有权限。

##### 2. 网页授权回调域名

在微信服务号请求用户网页授权之前，开发者需要先到[微信公众平台](https://mp.weixin.qq.com/)中的「设置与开发 - 账号设置 - 功能设置」的配置选项中，修改「网页授权域名」（如果没有看到这个选项，说明你的账号不支持此功能）。

> 请注意，这里填写的是域名（例如：`www.qq.com`），而不是URL，因此请勿加 http:// 等协议头；

授权回调域名配置规范为全域名，比如需要网页授权的域名为 `www.qq.com`

配置以后此域名下面的页面 `http://www.qq.com/music.html` 、 `http://www.qq.com/login.html` 都可以进行网页授权。但 `http://pay.qq.com` 、 `http://music.qq.com` 、 `http://qq.com` 无法进行网页鉴权。

如果服务号登录授权给了第三方开发者来进行管理，则不必做任何设置，由第三方平台代替服务号实现网页授权即可

##### 3. UnionId 机制

网页授权获取用户基本信息遵循 UnionID 机制。如果开发者有在多个服务号，或在服务号、移动应用、小程序等之间统一用户账号的需求，需要前往微信开放平台（open.weixin.qq.com）绑定服务号后，才可利用 UnionId 机制来满足上述需求。

更多详细介绍可查看[UnionId 机制详细介绍](/doc/oplatform/open/intro.md)

## 二、跳转授权链接

#### 构造链接

开发者应按照下述规则构造授权链接：

``` bash
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
```

各参数的解释如下：
| 参数 | 是否必须 | 说明 |
| --- | --- | --- |
| appid | 是 | 服务号的唯一标识（仅支持填写已认证的服务号 appid，其他类型的账号不支持） |
| redirect_uri | 是 | 授权后重定向的回调链接地址， 请使用 urlEncode 对链接进行处理；必须要和在微信公众平台「设置与开发 - 账号设置 - 功能设置 - 网页授权域名」的配置一致 |
| response_type | 是 | 返回类型，请填写code |
| scope | 是 | 应用授权作用域，snsapi_base （不弹出授权页面，直接跳转，只能获取用户openid），snsapi_userinfo （弹出授权页面，可通过openid拿到昵称、性别、所在地。并且， 即使在未关注的情况下，只要用户授权，也能获取其信息 ） |
| state | 否 | 重定向后会带上state参数，开发者可以填写a-zA-Z0-9的参数值，最多128字节 |
| #wechat_redirect | 是 | 无论直接打开还是做页面302重定向时候，必须带此参数 |
| forcePopup | 否 | 强制此次授权需要用户弹窗确认；默认为false；需要注意的是，若用户命中了特殊场景下的静默授权逻辑，则此参数不生效 |

目前网页授权有两个 scope，分别是：`snsapi_base` 和 `snsapi_userinfo`，解释如下：

- **snsapi_base**：用来获取进入页面的用户的openid的，并且是静默授权并自动跳转到回调页的。（不会弹出信息确认框,仅能获取用户 openid 等信息，无法获取昵称头像）
- **snsapi_userinfo**：用来获取用户的基本信息的。但这种授权需要用户手动同意。（由于用户同意过，所以无须依赖用户关注服务号，就可在授权后获取该用户的基本信息）

#### 链接例子

scope 为 `snsapi_base` 的示例

``` bash
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect
```

scope 为 `snsapi_userinfo` 的示例

``` bash
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx807d86fb6b3d4fd2&redirect_uri=http%3A%2F%2Fdevelopers.weixin.qq.com&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
```

#### 发起跳转

服务号配置了「网页授权域名」后，在配置的授权域名页面中，跳转至自己构造的授权链接页面。比较常见的有：

``` html
<a href="url">登录</a>
```

``` js
location.href = "url"
```

你可以结合业务，在用户交互时跳转或者网页初始化时直接判断跳转。

#### 注意事项

在跳转授权链接时，你需注意以下事项：

- 若提示“该链接无法访问”，请检查参数是否填写错误，是否拥有scope参数对应的授权作用域权限。
- 由于授权操作安全等级较高，所以在发起授权请求时，微信会对授权链接做正则强匹配校验，如果链接的参数顺序不对，授权页面将无法正常访问
- 跳转回调redirect_uri，应当使用https链接来确保授权code的安全性。

## 三、获取用户授权

当你做 `snsapi_base` 授权时，客户端默认同意。

当你做 `snsapi_userinfo` 授权时，有两种情况：

情况1：对于已关注服务号的用户，如果用户从服务号的会话或者自定义菜单进入本服务号的网页授权页，此时静默授权，无需用户在弹窗同意。

情况2：在非情况 1 的其他情况下，进行 `snsapi_userinfo` 授权时，新版微信客户端做了一些处理。

> 当你使用用户交互（点击按钮）跳转授权链接时，微信会拉起弹出窗口确认，如下图所示：

> ![](https://res.wx.qq.com/op_res/AHGc_PcTDNdCiIFozeUEbrz9lQ9Uhy6CdHsank0gWDh7rW-r6QaDTCmyRZ4nN-mKl_HLhrpGXFGrAgs0Yegt6Q)
> 
> 当你在用户未做任何交互动作，直接跳转授权链接时，网页将被固化，展示「点击访问完整网页」字样，微信不推荐打开网页就直接授权的业务场景。

如果用户同意授权，页面将跳转至授权链接的 `redirect_uri` 参数所示链接中，并附带 `code` 和 `state`

``` bash
redirect_uri/?code=CODE&state=STATE。
```

参数说明：

- **code**：作为换取access_token的票据，每次用户授权带上的code将不一样，code只能使用一次，5分钟未被使用自动过期。
- **state**：直接带上授权链接的 state 参数内容，用于防止越权行为。

当然，如果授权链接的参数不正确，用户将会看到错误提示，目前有下述错误，你需要根据说明完成调整。

| 返回码 | 说明 |
| --- | --- |
| 10003 | redirect_uri域名与后台配置不一致 |
| 10004 | 此服务号被封禁 |
| 10005 | 此服务号并没有这些scope的权限 |
| 10006 | 必须关注此测试号 |
| 10009 | 操作太频繁了，请稍后重试 |
| 10010 | scope不能为空 |
| 10011 | redirect_uri不能为空 |
| 10012 | appid不能为空|
| 10013 | state不能为空|
| 10015 | 服务号未授权第三方平台，请检查授权状态|
| 10016 | 不支持微信开放平台的Appid，请使用服务号Appid|

## 四、换取用户授权 Token

回调页面拿到 code 之后，应传递给后端服务器，后端服务需访问相应接口换取用户信息和授权 Token

#### 4.1 获取用户授权信息接口

通过 code 换取的是用户授权 access_token，它的权限范围仅限于该用户的信息获取，无法调用本文档之外的其他接口。此接口返回的 Token 信息可用范围如下：

在 `snsapi_base` 情况下，无法也不需要调用任何接口。

在 `snsapi_userinfo` 情况下，可以执行 获取用户信息、刷新 Token、检验 Token 有效性

``` bash
GET https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
```

**此接口频率限制：5 万/分钟**，提额可参考[网页授权接口调用频率提升指引](get_quota)

> 注意：由于服务号的 secret 和获取到的 access_token 安全级别都非常高，必须只保存在服务器，不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤，也必须从服务器发起。

请求参数

| 参数 | 是否必须 | 说明 |
| --- | --- | --- |
| appid | 是 | 服务号的唯一标识 |
| secret | 是 | 服务号的appsecret |
| code | 是 | 填写第一步获取的code参数 |
| grant_type | 是 | 填写为authorization_code    |

返回参数

| 参数 | 描述 |
| --- | --- |
| access_token | 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同 |
| expires_in | access_token接口调用凭证超时时间，单位（秒） |
| refresh_token | 用户刷新access_token |
| openid | 用户唯一标识，请注意，在未关注服务号时，用户访问服务号的网页，也会产生一个用户和服务号唯一的OpenID |
| scope | 用户授权的作用域，使用逗号（,）分隔 |
| is_snapshotuser | 是否为快照页模式虚拟账号，**只有当用户是快照页模式虚拟账号时返回，值为1** |
| unionid | 用户统一标识（针对一个微信开放平台账号下的应用，同一用户的 unionid 是唯一的），**只有当scope为"snsapi_userinfo"时返回** |

返回示例

```json
正确时返回内容：

{
  "access_token":"ACCESS_TOKEN",
  "expires_in":7200,
  "refresh_token":"REFRESH_TOKEN",
  "openid":"OPENID",
  "scope":"SCOPE",
  "is_snapshotuser": 1,
  "unionid": "UNIONID"
}
```

```json
错误时返回内容：

{"errcode":40029,"errmsg":"invalid code"}
```

#### 4.2 刷新授权 Token

由于 `access_token` 拥有较短的有效期，当 `access_token` 超时后，可以使用 `refresh_token` 进行刷新，`refresh_token` 有效期为30天，当 `refresh_token` 失效之后，需要用户重新授权。

``` bash
GET https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
```

**此接口频率限制：5 万/分钟**，提额可参考[网页授权接口调用频率提升指引](get_quota)

请求参数

| 参数 | 是否必须 | 说明 |
| --- | --- | --- |
| appid | 是 | 服务号的唯一标识 |
| grant_type | 是 | 填写为refresh_token |
| refresh_token | 是 | 填写用户授权信息接口获取到的 refresh_token 返回参数   |

返回参数

| 参数 | 描述 |
| --- | --- |
| access_token | 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同 |
| expires_in | access_token接口调用凭证超时时间，单位（秒） |
| refresh_token | 用户刷新access_token |
| openid | 用户唯一标识 |
| scope | 用户授权的作用域，使用逗号（,）分隔 |

返回示例

```json
正确时返回内容：

{ 
  "access_token":"ACCESS_TOKEN",
  "expires_in":7200,
  "refresh_token":"REFRESH_TOKEN",
  "openid":"OPENID",
  "scope":"SCOPE" 
}
```

```json
错误时返回内容：

{"errcode":-1,"errmsg":"invalid Token"}
```

#### 4.3 检验 Token 有效性

可使用此接口，检验上面的授权 access_token 是否有效

``` bash
GET https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID
```

请求参数

| 参数 | 描述 |
| --- | --- |
| access_token | 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同 |
| openid | 用户的唯一标识     |

返回说明

```json
正确时返回内容：
{ "errcode":0,"errmsg":"ok"}
```

```json
错误时返回内容：
{"errcode":-1,"errmsg":"invalid Token"}
```

## 五、拉取用户信息

如果网页授权作用域为 `snsapi_userinfo`，则此时可以通过 `access_token` 和 `openid` 拉取用户信息。

``` bash
GET https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
```

**此接口频率限制：5 万/分钟**，提额可参考[网页授权接口调用频率提升指引](get_quota)

请求参数

| 参数 | 描述 |
| --- | --- |
| access_token | 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同 |
| openid | 用户的唯一标识 |
| lang | 返回国家地区语言版本，zh_CN 简体，zh_TW 繁体，en 英语 |

返回参数

| 参数 | 描述 |
| --- | --- |
| openid | 用户的唯一标识 |
| nickname | 用户昵称 |
| sex | 用户的性别，值为1时是男性，值为2时是女性，值为0时是未知 |
| province | 用户个人资料填写的省份 |
| city | 普通用户个人资料填写的城市 |
| country | 国家，如中国为CN |
| headimgurl | 用户头像，最后一个数值代表正方形头像大小（有0、46、64、96、132数值可选，0代表640\*640正方形头像），用户没有头像时该项为空。若用户更换头像，原有头像URL将失效。 |
| privilege | 用户特权信息，json 数组，如微信沃卡用户为（chinaunicom） |
| unionid | 只有在用户将服务号绑定到微信开放平台账号后，才会出现该字段。 |

返回示例

```json
正确时返回内容：

{   
  "openid": "OPENID",
  "nickname": NICKNAME,
  "sex": 1,
  "province":"PROVINCE",
  "city":"CITY",
  "country":"COUNTRY",
  "headimgurl":"https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
  "privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
```

```json
错误时返回内容：

{"errcode":40003,"errmsg":" invalid openid "}
```

## 六、授权用户信息变更

平台建议开发者配置服务器消息推送监听，以方便第一时间获取相关变更时间信息，具体对接可查看[消息推送](get_quota)指引文档。

在用户授权的信息场景下，有如下事件：

1、	授权用户资料变更：当部分用户的资料存在风险时，平台会对用户资料进行清理，并通过消息推送服务器通知最近30天授权过的服务号开发者，我们建议开发者留意响应该事件，及时主动更新或清理用户的头像及昵称，降低风险。  
2、	授权用户资料撤回：当用户撤回授权信息时，平台会通过消息推送服务器通知给服务号开发者，请开发者注意及时删除用户信息。    
3、 授权用户完成注销：当授权用户完成注销后，平台会通过消息推送服务器通知给服务号开发者，请依法依规及时履行相应个人信息保护义务，保护用户权益。    

#### 事件推送示例：

XML 格式

```xml
<xml>
    <ToUserName><![CDATA[gh_870882ca4b1]]></ToUserName>
    <FromUserName><![CDATA[owAqB1v0ahK_Xlc7GshIDdf2yf7E]]></FromUserName>
    <CreateTime>1626857200</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[user_authorization_revoke]]></Event>
    <OpenID><![CDATA[owAqB1nqaOYYWl0Ng484G2z5NIwU]]></OpenID>
    <AppID><![CDATA[wx13974bf780d3dc89]]></AppID>
    <RevokeInfo><![CDATA[1]]></RevokeInfo>
</xml>
```

JSON 格式

``` json
{
    "ToUserName": "gh_870882ca4b1",
    "FromUserName": "oaKk346BaWE-eIn4oSRWbaM9vR7s",
    "CreateTime": 1627359464,
    "MsgType": "event",
    "Event": "user_authorization_revoke",
    "OpenID": "oaKk343WOktAaT2ygsX138BGblrg",
    "AppID": "wx13974bf780d3dc89",
    "RevokeInfo": "201",
}
```

#### 事件字段定义

| 属性 | 类型 | 说明 |
| ---- | ---- | ---- |
| ToUserName | string |	服务号的UserName |
| FromUserName | string | 平台推送服务UserName |
| MsgType |	string | 默认为：event |
| Event | string |	user_info_modified：用户资料变更，user_authorization_revoke：用户撤回，user_authorization_cancellation：用户完成注销； |
| CreateTime |	number | 发送时间 |
| OpenID | string |	授权用户OpenID |
| AppID | string |	服务号的AppID |
| RevokeInfo | string |	用户撤回的H5授权信息，201:地址,202:发票信息,203:卡券信息,204:麦克风,205:昵称和头像,206:位置信息,207:选中的图片或视频 |
