编程指南社区OAuth 2.0 接入文档
OAuth 2.0 接入文档
编程指南开放平台提供 OAuth 2.0 授权登录能力,允许第三方网站引导用户使用编程指南账号进行登录,获取用户基本信息。
简介
编程指南 OAuth 2.0 授权登录采用标准的 Authorization Code(授权码) 模式。第三方应用接入后,用户可以在你的网站上通过编程指南账号一键登录,无需再次注册。
授权成功后,你的应用可以获取到以下用户信息:
- 用户唯一标识(uid)
- 用户昵称(nickname)
- 用户头像(avatar)
- 用户简介(desc)
术语说明
| 术语 | 说明 |
|---|---|
client_id |
应用唯一标识,创建应用后获得 |
client_secret |
应用密钥,创建应用时获得(仅显示一次),用于后端换取 token |
redirect_uri |
授权回调地址,用户授权成功后跳转的地址 |
authorization_code |
授权码(code),用户同意授权后生成,有效期 5 分钟,一次性使用 |
access_token |
访问令牌,调用接口的凭证,有效期 2 小时 |
refresh_token |
刷新令牌,用于续期 access_token,有效期 30 天 |
scope |
授权范围,当前支持 user_info(获取用户基本信息) |
授权流程
整体流程
用户点击登录 → 跳转编程指南授权页 → 用户同意授权 → 回调你的网站(带 code) → 后端用 code 换 token → 获取用户信息
注意: code 换 token 以及获取用户信息必须在 你的服务端 完成,不要在前端暴露 client_secret。
第一步:引导用户授权
在你的网站登录页面放置「使用编程指南登录」按钮,用户点击后跳转到编程指南授权页面。
请求地址
https://code-guide.vip/oauth/consent
请求方式
GET 浏览器跳转(Query 参数)
请求 Header
该接口为浏览器跳转,无需设置请求头。
请求参数
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
client_id |
string | 是 | 应用唯一标识 |
redirect_uri |
string | 是 | 授权回调地址,需与创建应用时填写的一致,需做 URL 编码 |
response_type |
string | 是 | 固定值 code |
scope |
string | 是 | 授权范围,固定值 user_info |
state |
string | 推荐 | 随机字符串,用于防止 CSRF 攻击,授权完成后会原样返回 |
请求示例
https://code-guide.vip/oauth/consent?client_id=your_client_id&redirect_uri=https%3A%2F%2Fyoursite.com%2Fcallback&response_type=code&scope=user_info&state=random_string
应答参数
该接口为浏览器跳转,用户同意授权后会重定向到你的 redirect_uri,并在 URL 中携带以下参数:
| 字段 | 类型 | 描述 |
|---|---|---|
code |
string | 授权码,有效期 5 分钟,一次性使用 |
state |
string | 你传入的 state 参数,原样返回 |
应答示例
授权成功回调:
https://yoursite.com/callback?code=AUTHORIZATION_CODE&state=random_string
用户拒绝授权:
https://yoursite.com/callback?error=access_denied&state=random_string
第二步:获取授权码
用户在编程指南授权页同意授权后,浏览器会被重定向到你填写的 redirect_uri,并携带 code 和 state 参数。
回调地址
https://yoursite.com/callback(你在创建应用时填写的 redirect_uri)
回调方式
GET 浏览器 302 重定向(Query 参数)
回调参数
| 字段 | 类型 | 描述 |
|---|---|---|
code |
string | 授权码,有效期 5 分钟,一次性使用。获取后需立即在服务端换取 access_token |
state |
string | 第一步传入的 state 参数,原样返回,用于防止 CSRF 攻击 |
error |
string | 错误码(仅用户拒绝授权时返回),值为 access_denied |
注意: 授权码 code 有效期为 5 分钟,且只能使用一次。请在获取后立即在服务端换取 access_token。
回调示例
授权成功:
https://yoursite.com/callback?code=AUTHORIZATION_CODE&state=random_string
用户拒绝授权:
https://yoursite.com/callback?error=access_denied&state=random_string
前端处理示例
// 在你的 callback 页面解析回调参数
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');
const error = params.get('error');
// 验证 state 防止 CSRF
const savedState = sessionStorage.getItem('oauth_state');
if (state !== savedState) {
alert('安全验证失败');
return;
}
if (error) {
console.log('用户拒绝授权:', error);
return;
}
if (code) {
fetch('/api/auth/code-guide/callback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, state })
})
.then(res => res.json())
.then(data => console.log('登录成功:', data));
}
第三步:获取访问令牌
在你的 服务端,使用授权码 code 换取 access_token。
请求地址
https://api.code-guide.autogptai.club/oauth/token
请求方式
POST Request-Body(application/x-www-form-urlencoded)
请求 Header
| 字段 | 类型 | 描述 |
|---|---|---|
Content-Type |
string | 请求消息类型,固定值:application/x-www-form-urlencoded |
请求参数
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
grant_type |
string | 是 | 固定值 authorization_code |
code |
string | 是 | 上一步获取的授权码 |
client_id |
string | 是 | 应用唯一标识 |
client_secret |
string | 是 | 应用密钥 |
redirect_uri |
string | 是 | 授权回调地址,需与第一步一致 |
请求示例
curl -X POST https://api.code-guide.autogptai.club/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "redirect_uri=https://yoursite.com/callback"
应答参数
| 字段 | 类型 | 描述 |
|---|---|---|
code |
number | 返回码,1 表示成功,0 表示失败 |
msg |
string | 返回信息 |
data |
object | 返回数据对象 |
└ access_token |
string | 访问令牌,有效期 2 小时 |
└ token_type |
string | 令牌类型,固定值 Bearer |
└ expires_in |
number | 过期时间,单位:秒(默认 7200) |
└ refresh_token |
string | 刷新令牌,有效期 30 天 |
└ scope |
string | 授权范围 |
应答示例
成功响应:
{
"code": 1,
"msg": "success",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 7200,
"refresh_token": "a1b2c3d4e5f6...",
"scope": "user_info"
}
}
失败响应:
{
"code": 0,
"msg": "invalid authorization code",
"data": null
}
注意: code 换 token 必须在 你的服务端 完成,不要在前端暴露 client_secret。
第四步:获取用户信息
使用 access_token 调用以下接口获取用户基本信息。
请求地址
https://api.code-guide.autogptai.club/oauth/userinfo
请求方式
GET
请求 Header
| 字段 | 类型 | 描述 |
|---|---|---|
Authorization |
string | 授权 Access-Token,格式:Bearer {access_token},获取方法见第三步 |
请求参数
该接口无需请求参数,仅通过 Header 中的 Access-Token 鉴权。
请求示例
curl https://api.code-guide.autogptai.club/oauth/userinfo \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
应答参数
| 字段 | 类型 | 描述 |
|---|---|---|
code |
number | 返回码,1 表示成功,0 表示失败 |
msg |
string | 返回信息 |
data |
object | 用户信息对象 |
└ uid |
number | 用户唯一标识 |
└ nickname |
string | 用户昵称 |
└ avatar |
string | 用户头像 URL |
└ desc |
string | 用户个人简介 |
应答示例
成功响应:
{
"code": 1,
"msg": "success",
"data": {
"uid": 10001,
"nickname": "张三",
"avatar": "https://xxx/avatar.jpg",
"desc": "全栈开发者"
}
}
失败响应:
{
"code": 0,
"msg": "invalid or expired access_token",
"data": null
}
API 参考
所有 API 的基础地址为 https://api.code-guide.autogptai.club。响应格式统一为:{ "code": 1, "msg": "success", "data": ... },其中 code=1 表示成功,code=0 表示失败。
获取 Access Token
请求地址
https://api.code-guide.autogptai.club/oauth/token
请求方式
POST Request-Body(application/x-www-form-urlencoded)
请求 Header
| 字段 | 类型 | 描述 |
|---|---|---|
Content-Type |
string | 请求消息类型,固定值:application/x-www-form-urlencoded |
请求参数
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
grant_type |
string | 是 | 固定值 authorization_code |
code |
string | 是 | 用户授权后获取的授权码,有效期 5 分钟,一次性使用 |
client_id |
string | 是 | 应用唯一标识 |
client_secret |
string | 是 | 应用密钥 |
redirect_uri |
string | 是 | 授权回调地址,需与第一步一致 |
请求示例
curl -X POST https://api.code-guide.autogptai.club/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "redirect_uri=https://yoursite.com/callback"
import requests
url = "https://api.code-guide.autogptai.club/oauth/token"
data = {
"grant_type": "authorization_code",
"code": "AUTHORIZATION_CODE",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"redirect_uri": "https://yoursite.com/callback"
}
response = requests.post(url, data=data)
print(response.json())
const response = await fetch(
'https://api.code-guide.autogptai.club/oauth/token',
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: 'AUTHORIZATION_CODE',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
redirect_uri: 'https://yoursite.com/callback'
})
}
);
const result = await response.json();
console.log(result);
import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newHttpClient();
String body = "grant_type=authorization_code"
+ "&code=AUTHORIZATION_CODE"
+ "&client_id=YOUR_CLIENT_ID"
+ "&client_secret=YOUR_CLIENT_SECRET"
+ "&redirect_uri=https://yoursite.com/callback";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.code-guide.autogptai.club/oauth/token"))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
应答参数
| 字段 | 类型 | 描述 |
|---|---|---|
code |
number | 返回码,1 表示成功,0 表示失败 |
msg |
string | 返回信息 |
data |
object | 返回数据对象 |
└ access_token |
string | 访问令牌,有效期 2 小时 |
└ token_type |
string | 令牌类型,固定值 Bearer |
└ expires_in |
number | 过期时间,单位:秒(默认 7200) |
└ refresh_token |
string | 刷新令牌,有效期 30 天 |
└ scope |
string | 授权范围 |
应答示例
成功响应:
{
"code": 1,
"msg": "success",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 7200,
"refresh_token": "a1b2c3d4e5f6...",
"scope": "user_info"
}
}
失败响应:
{
"code": 0,
"msg": "invalid authorization code",
"data": null
}
获取用户信息
请求地址
https://api.code-guide.autogptai.club/oauth/userinfo
请求方式
GET
请求 Header
| 字段 | 类型 | 描述 |
|---|---|---|
Authorization |
string | 授权 Access-Token,格式:Bearer {access_token},获取方法见接口【获取 Access Token】 |
请求参数
该接口无需请求参数,仅通过 Header 中的 Access-Token 鉴权。
请求示例
curl https://api.code-guide.autogptai.club/oauth/userinfo \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
import requests
url = "https://api.code-guide.autogptai.club/oauth/userinfo"
headers = {
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
response = requests.get(url, headers=headers)
print(response.json())
const response = await fetch(
'https://api.code-guide.autogptai.club/oauth/userinfo',
{
headers: { 'Authorization': 'Bearer YOUR_ACCESS_TOKEN' }
}
);
const result = await response.json();
console.log(result);
import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.code-guide.autogptai.club/oauth/userinfo"))
.header("Authorization", "Bearer YOUR_ACCESS_TOKEN")
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
应答参数
| 字段 | 类型 | 描述 |
|---|---|---|
code |
number | 返回码,1 表示成功,0 表示失败 |
msg |
string | 返回信息 |
data |
object | 用户信息对象 |
└ uid |
number | 用户唯一标识 |
└ nickname |
string | 用户昵称 |
└ avatar |
string | 用户头像 URL |
└ desc |
string | 用户个人简介 |
应答示例
成功响应:
{
"code": 1,
"msg": "success",
"data": {
"uid": 10001,
"nickname": "张三",
"avatar": "https://xxx/avatar.jpg",
"desc": "全栈开发者"
}
}
失败响应:
{
"code": 0,
"msg": "invalid or expired access_token",
"data": null
}
刷新 Access Token
access_token 过期后,使用 refresh_token 获取新的 access_token,无需用户重新授权。
请求地址
https://api.code-guide.autogptai.club/oauth/token/refresh
请求方式
POST Request-Body(application/x-www-form-urlencoded)
请求 Header
| 字段 | 类型 | 描述 |
|---|---|---|
Content-Type |
string | 请求消息类型,固定值:application/x-www-form-urlencoded |
请求参数
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
refresh_token |
string | 是 | 之前获取的 refresh_token |
client_id |
string | 是 | 应用唯一标识 |
client_secret |
string | 是 | 应用密钥 |
请求示例
curl -X POST https://api.code-guide.autogptai.club/oauth/token/refresh \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "refresh_token=YOUR_REFRESH_TOKEN" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
import requests
url = "https://api.code-guide.autogptai.club/oauth/token/refresh"
data = {
"refresh_token": "YOUR_REFRESH_TOKEN",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
response = requests.post(url, data=data)
print(response.json())
const response = await fetch(
'https://api.code-guide.autogptai.club/oauth/token/refresh',
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
refresh_token: 'YOUR_REFRESH_TOKEN',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET'
})
}
);
const result = await response.json();
console.log(result);
import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newHttpClient();
String body = "refresh_token=YOUR_REFRESH_TOKEN"
+ "&client_id=YOUR_CLIENT_ID"
+ "&client_secret=YOUR_CLIENT_SECRET";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.code-guide.autogptai.club/oauth/token/refresh"))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
应答参数
| 字段 | 类型 | 描述 |
|---|---|---|
code |
number | 返回码,1 表示成功,0 表示失败 |
msg |
string | 返回信息 |
data |
object | 返回数据对象 |
└ access_token |
string | 新的访问令牌,有效期 2 小时 |
└ token_type |
string | 令牌类型,固定值 Bearer |
└ expires_in |
number | 过期时间,单位:秒(默认 7200) |
└ refresh_token |
string | 新的刷新令牌(滚动刷新),有效期 30 天 |
└ scope |
string | 授权范围 |
应答示例
成功响应:
{
"code": 1,
"msg": "success",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...(new)",
"token_type": "Bearer",
"expires_in": 7200,
"refresh_token": "x9y8z7w6v5u4...(new)",
"scope": "user_info"
}
}
失败响应:
{
"code": 0,
"msg": "invalid or expired refresh_token",
"data": null
}
撤销令牌
当用户退出登录或解除绑定时,应主动撤销令牌。
请求地址
https://api.code-guide.autogptai.club/oauth/token/revoke
请求方式
POST Request-Body(application/x-www-form-urlencoded)
请求 Header
| 字段 | 类型 | 描述 |
|---|---|---|
Content-Type |
string | 请求消息类型,固定值:application/x-www-form-urlencoded |
请求参数
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
token |
string | 是 | 要撤销的令牌(access_token 或 refresh_token) |
client_id |
string | 是 | 应用唯一标识 |
client_secret |
string | 是 | 应用密钥 |
请求示例
curl -X POST https://api.code-guide.autogptai.club/oauth/token/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=ACCESS_TOKEN_OR_REFRESH_TOKEN" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
import requests
url = "https://api.code-guide.autogptai.club/oauth/token/revoke"
data = {
"token": "ACCESS_TOKEN_OR_REFRESH_TOKEN",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
response = requests.post(url, data=data)
print(response.json())
const response = await fetch(
'https://api.code-guide.autogptai.club/oauth/token/revoke',
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
token: 'ACCESS_TOKEN_OR_REFRESH_TOKEN',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET'
})
}
);
const result = await response.json();
console.log(result);
import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newHttpClient();
String body = "token=ACCESS_TOKEN_OR_REFRESH_TOKEN"
+ "&client_id=YOUR_CLIENT_ID"
+ "&client_secret=YOUR_CLIENT_SECRET";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.code-guide.autogptai.club/oauth/token/revoke"))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
应答参数
| 字段 | 类型 | 描述 |
|---|---|---|
code |
number | 返回码,1 表示成功,0 表示失败 |
msg |
string | 返回信息 |
data |
null | 该接口无返回数据 |
应答示例
成功响应:
{
"code": 1,
"msg": "success",
"data": null
}
失败响应:
{
"code": 0,
"msg": "invalid token",
"data": null
}
快速开始
创建应用
- 登录编程指南,进入 设置 → 开发者中心
- 点击「创建应用」,填写应用名称、Logo、简介和回调地址
- 创建成功后,妥善保存 Client ID 和 Client Secret(Secret 仅显示一次)
- 在你的网站登录页添加「使用编程指南登录」按钮
安全提醒: Client Secret 是应用的重要凭证,请勿在前端代码、版本控制或公开场合暴露。
前端集成示例
在你的登录页面添加按钮,点击后跳转授权页:
<!-- 编程指南登录按钮 -->
<button onclick="loginWithCodeGuide()">使用编程指南登录</button>
<script>
function loginWithCodeGuide() {
const clientId = 'YOUR_CLIENT_ID';
const redirectUri = encodeURIComponent('https://yoursite.com/callback');
const state = Math.random().toString(36).substring(2);
// 保存 state 到 sessionStorage,回调时验证
sessionStorage.setItem('oauth_state', state);
window.location.href =
'https://code-guide.vip/oauth/consent' +
'?client_id=' + clientId +
'&redirect_uri=' + redirectUri +
'&response_type=code' +
'&scope=user_info' +
'&state=' + state;
}
</script>
在回调页面处理授权结果:
// callback 页面
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');
const error = params.get('error');
// 验证 state 防止 CSRF
const savedState = sessionStorage.getItem('oauth_state');
if (state !== savedState) {
alert('安全验证失败');
return;
}
if (error) {
// 用户拒绝授权
console.log('授权被拒绝:', error);
return;
}
if (code) {
// 将 code 发送到你的后端换取 token
fetch('/api/auth/code-guide/callback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, state })
})
.then(res => res.json())
.then(data => {
// 登录成功,保存用户信息
console.log('登录成功:', data);
});
}
后端集成示例
以 Node.js(Express)为例:
const express = require('express');
const axios = require('axios');
const app = express();
const CLIENT_ID = 'your_client_id';
const CLIENT_SECRET = 'your_client_secret';
const REDIRECT_URI = 'https://yoursite.com/callback';
const API_BASE = 'https://api.code-guide.autogptai.club';
app.use(express.json());
// 处理回调
app.post('/api/auth/code-guide/callback', async (req, res) => {
const { code } = req.body;
try {
// 第一步:用 code 换取 access_token
const tokenRes = await axios.post(
API_BASE + '/oauth/token',
new URLSearchParams({
grant_type: 'authorization_code',
code,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: REDIRECT_URI
}).toString(),
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
);
const { access_token, refresh_token } = tokenRes.data.data;
// 第二步:用 access_token 获取用户信息
const userRes = await axios.get(API_BASE + '/oauth/userinfo', {
headers: { Authorization: 'Bearer ' + access_token }
});
const userInfo = userRes.data.data;
// 第三步:在你的系统中查找或创建用户,生成登录态
// ...
res.json({
success: true,
user: userInfo,
access_token,
refresh_token
});
} catch (error) {
console.error('OAuth 回调处理失败:', error);
res.status(500).json({ success: false, message: '登录失败' });
}
});
app.listen(3000);
常见问题
Q:code 过期了怎么办?
授权码(code)有效期为 5 分钟,且只能使用一次。过期后需要引导用户重新授权获取新的 code。
Q:access_token 过期了怎么办?
access_token 有效期为 2 小时。过期后使用 refresh_token 调用 /oauth/token/refresh 接口获取新的 access_token,无需用户重新授权。
Q:refresh_token 也过期了怎么办?
refresh_token 有效期为 30 天。过期后需要引导用户重新授权。
Q:redirect_uri 必须完全一致吗?
是的。授权请求中的 redirect_uri 必须与在开发者中心创建应用时填写的回调地址完全匹配,否则授权将失败。
Q:可以在前端直接用 code 换 token 吗?
不可以。 换取 token 需要使用 client_secret,该密钥不能暴露在前端代码中。请在你的服务端完成 code 换 token 的操作。
Q:如何保证安全性?
- 始终使用
state参数防止 CSRF 攻击 - client_secret 只在服务端使用,不要暴露到前端
- 使用 HTTPS 传输所有请求
- 不再使用的令牌应主动撤销
更新日志
2026-03-20
- 发布 OAuth 2.0 授权登录 v1.0
- 支持 Authorization Code 授权码模式
- 支持获取用户基本信息(uid、昵称、头像、简介)
- 提供 access_token 刷新和撤销能力

评论 0