基于扣子 OAuth JWT 鉴权实现多用户会话隔离的完整解决方案。每个访客拥有独立的聊天记录和用户变量,互不干扰。
E:\coze\
├── server.py # Flask 后端服务(提供 API + 静态页面)
├── coze_oauth.py # OAuth JWT 封装(基于 cozepy SDK)
├── index.html # 前端页面
├── index.js # 前端逻辑(Chat SDK 集成)
├── main.css # 样式文件
├── requirements.txt # Python 依赖
├── .env # 配置文件(OAuth 密钥等)
└── README.md # 本文档
✅ 会话隔离:不同用户的聊天记录、用户变量完全隔离
✅ 自动刷新:Token 过期自动续期,用户无感知
✅ 简单部署:单个 Flask 服务同时提供前后端
✅ 官方 SDK:使用 cozepy 官方 SDK,稳定可靠
- Python 3.8+
- 扣子账号及 OAuth JWT 应用
- 访问 https://www.coze.cn/open/oauth/apps
- 创建"服务应用(JWT)"类型的 OAuth 应用
- 生成公私钥对并上传公钥:
# 生成私钥 openssl genrsa -out coze_private.pem 2048 # 导出公钥 openssl rsa -in coze_private.pem -pubout -out coze_public.pem
- 记录以下信息:
client_id(应用 ID)public_key_id(上传公钥后生成的 ID)- 私钥内容(
coze_private.pem)
在项目根目录创建 .env 文件(顶格写,不要有行首空格):
COZE_JWT_OAUTH_CLIENT_ID=你的client_id
COZE_JWT_OAUTH_PUBLIC_KEY_ID=你的public_key_id
COZE_JWT_OAUTH_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD...
(完整私钥内容,保持 PEM 格式换行)
...
-----END PRIVATE KEY-----"
COZE_API_BASE=https://api.coze.cn或使用私钥文件:
COZE_JWT_OAUTH_CLIENT_ID=你的client_id
COZE_JWT_OAUTH_PUBLIC_KEY_ID=你的public_key_id
COZE_JWT_OAUTH_PRIVATE_KEY_FILE_PATH=./coze_private.pem
COZE_API_BASE=https://api.coze.cnpip install -r requirements.txt编辑 index.js,修改第 1 行:
const BOT_ID = '你的Bot ID'; // 在扣子控制台的 Bot 页面获取flask --app server run或指定端口:
flask --app server run --port 8080浏览器打开 http://127.0.0.1:5000/,聊天窗口应正常加载。
- 正常浏览器访问
http://127.0.0.1:5000/,发送消息 A - 隐私/无痕模式访问同一地址,发送消息 B
- 两个窗口的聊天记录完全独立,互不可见 ✅
用户访问 → 前端生成唯一 UID(localStorage)
↓
POST /api/chat-token {userId: "user_xxx"}
↓
后端签发 JWT(payload 含 session_name=user_xxx)
↓
调用 Coze OAuth API 换取 access_token
↓
前端用 token 初始化 Chat SDK
↓
Coze 按 session_name 隔离会话
session_name:JWT payload 里的字段,Coze 用它区分不同子会话aud:必须是api.coze.cn(域名,不带https://)token_type:前端 auth 配置必须用'token',不是'jwt'
为指定用户签发 OAuth access token。
请求:
{
"userId": "user_d36f2e58-610b-4d80-93c4-3c33a87b2855"
}响应:
{
"token": "czs_xxx...",
"token_type": "Bearer",
"expires_in": 1763662021,
"expires_at": 3527320440
}健康检查接口。
响应:
{
"status": "ok"
}修改 index.js 的 BOT_ID 即可。
- 复制
index.html→bot2.html - 复制
index.js→bot2.js - 修改
bot2.html引用bot2.js:<script src="./bot2.js"></script>
- 在
bot2.js里改BOT_ID - 访问
http://127.0.0.1:5000/bot2.html
后端 /api/chat-token 无需改动,可复用。
原因:公私钥不匹配或 OAuth 应用配置错误。
解决:
- 确认
.env里的public_key_id和扣子控制台一致 - 验证私钥对应的公钥是否已正确上传
- 查看 Flask 日志里的
[DEBUG]信息
原因:auth.type 配置错误或 token 获取失败。
解决:
- 确认
index.js里auth.type是'token' - 打开浏览器 F12 → Network,检查
/api/chat-token是否返回 200 - 查看 Console 是否有
[DEBUG] Got token日志
原因:未正确传递 session_name 或使用了旧的 Access Token 鉴权。
解决:
- 确认
coze_oauth.py调用了jwt_app.get_access_token(session_name=...) - 清除浏览器 localStorage,重新生成 UID
原因:onRefreshToken 未配置或实现错误。
解决:
- 确认
index.js里auth.onRefreshToken存在且正确调用fetchChatToken
- 私钥保护:
.env和.pem文件只放服务端,添加到.gitignore - HTTPS 部署:生产环境必须使用 HTTPS
- CORS 配置:如前后端分离部署,正确配置 CORS 白名单
- Token 有效期:可通过
COZE_ACCESS_TOKEN_TTL环境变量调整(默认 3600 秒)
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:8080 server:appserver {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}创建 Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8080", "server:app"]构建运行:
docker build -t coze-oauth-app .
docker run -p 8080:8080 --env-file .env coze-oauth-app- 后端:Flask + cozepy SDK
- 前端:Coze Chat SDK (WebChatClient)
- 鉴权:OAuth 2.0 JWT Bearer Token
- 会话隔离:
session_name参数
如需更新:
- 更换 Bot:修改
index.js的BOT_ID - 更换 OAuth 应用:更新
.env配置并重启服务 - 升级依赖:
pip install --upgrade cozepy
MIT License
如有问题,请参考扣子官方文档或提交 Issue。