跳转到内容

认证与授权

认证与授权 是 RTC Agent 的安全基石。用户通过 OAuth2 授权码模式登录,系统使用双令牌(Access + Refresh)维持会话,支持多设备并行登录,令牌自动刷新——用户只需登录一次,即可长期使用。

步骤说明
1用户点击登录按钮,弹出登录对话框
2对话框通过 iframe 展示 OAuth2 Provider 的授权页面
3用户在 Provider 页面完成授权(如 GitHub、Google 等)
4授权成功后,系统获取授权码并换取令牌
5令牌存储到浏览器本地,登录完成

💡 设计原则:登录流程完全委托给 OAuth2 Provider——RTC Agent 不接触用户密码,安全由 Provider 保障。

令牌有效期用途存储方式
🔑 Access Token1 小时访问 API 和 WebSocket明文(短有效期,风险可控)
🔄 Refresh Token30 天刷新 Access Token仅存哈希值(明文一次性返回后丢弃)

📌 安全要点:Refresh Token 的明文只在签发时返回一次,之后服务端仅保存哈希。即使浏览器存储被泄露,攻击者也无法长期冒充用户。

系统在多个时机自动刷新令牌,用户完全无感知:

触发时机说明
⏱️ 令牌即将过期到期前 5 分钟自动刷新,避免请求中断
📱 页面切回前台从后台切回时检查令牌状态,过期则立即刷新
🔌 建立 WebSocket连接时按需刷新令牌,确保令牌有效

💡 刷新失败不会让用户停留在”半死不活”的状态——系统会直接退出登录,引导用户重新认证。

概念说明
🔖 设备 ID浏览器唯一标识(UUID),首次访问时自动生成
📛 设备名根据操作系统自动推断(如”Mac”、“Windows PC”、“Linux PC”)
🔀 多设备同一用户可在多个设备独立登录,互不影响

📌 每个设备的令牌相互独立——在设备 A 上退出登录不会影响设备 B 的会话。

阶段说明
🔗 建连WebSocket 连接时携带 Access Token 进行认证
✅ 验证服务端校验令牌有效性,无效则拒绝连接
📡 订阅认证通过后订阅用户专属频道,接收实时消息
🔁 断线连接断开后需重新认证,确保安全性

💡 频道隔离:每个用户只能接收自己频道内的消息,无法访问他人数据。

约束机制目的
🛡️ CSRF 防护OAuth2 授权时使用 state 参数防止跨站请求伪造攻击
🏷️ 频道隔离用户只能订阅自己的专属频道防止数据泄露和越权访问
💾 令牌存储令牌仅存储在浏览器本地不传输到第三方,降低泄露风险
🔑 刷新令牌明文一次性返回,服务端仅存哈希即使存储泄露也无法长期使用
场景用户看到的行为
❌ 授权失败登录对话框显示错误信息,可点击重试
⏱️ 令牌过期且刷新失败自动退出登录,显示登录页面
🔌 WebSocket 认证失败连接断开,提示用户重新登录

RTC Agent Server 本身是 OAuth2 消费方——它需要连接一个 OAuth2 提供方来完成用户认证。开发环境内置了 mock-oauth2 作为示例提供方;生产部署时,你需要提供自己的 OAuth2 服务。

💡 RTC Agent Server 负责签发 JWT 令牌和管理设备;你的 OAuth2 服务只负责验证用户身份并返回用户信息。

你的 OAuth2 服务只需实现 2 个端点

端点 1:授权页面 — GET /oauth2/authorize

Section titled “端点 1:授权页面 — GET /oauth2/authorize”

浏览器直接访问(通过 iframe 加载),用于展示登录/授权 UI。

请求(RTC Agent Server 拼接后由浏览器访问):

GET /oauth2/authorize?state=<hex>&client_id=<id>&redirect_uri=<uri>
参数说明
state防 CSRF 随机串,必须原样传回
client_id客户端标识
redirect_uri授权成功后的回调地址

行为要求

  1. 展示登录/授权页面(可以是你的现有登录系统)
  2. 用户授权成功后,生成一个短期、一次性的授权码(code)
  3. HTTP 302 重定向到 redirect_uri,query string 中携带 codestate
Location: <redirect_uri>?code=<code>&state=<state>

页面约束

  • 页面会在 iframe 中加载,不能设置 X-Frame-Options: DENY 或限制性的 Content-Security-Policy: frame-ancestors
  • Content-Type 为 text/html; charset=utf-8

端点 2:代码交换 — POST /oauth2/token/exchange

Section titled “端点 2:代码交换 — POST /oauth2/token/exchange”

RTC Agent Server 服务端直接调用(server-to-server),用授权码换取用户身份信息。

请求

POST /oauth2/token/exchange
Content-Type: application/x-www-form-urlencoded
Accept: application/json
client_id=<id>&client_secret=<secret>&code=<code>&redirect_uri=<uri>

成功响应(200):

{
"provider_user_id": "user-12345",
"username": "张三",
"email": "zhangsan@example.com",
"avatar_url": "https://example.com/avatar.png"
}
字段必填说明
provider_user_id用户在你系统中的稳定唯一标识,同一用户必须始终返回相同值
username可选显示名称
email可选邮箱
avatar_url可选头像 URL

错误响应

{
"error": "invalid_client",
"error_description": "client_id 或 client_secret 错误"
}
HTTP 状态码error含义
400invalid_request缺少或非法参数
400invalid_grant授权码无效、已使用或已过期
401invalid_client客户端凭证错误
500server_error服务端内部错误
约束说明
一次性同一 code 只能交换一次
短期有效建议 10 分钟内过期
绑定用户code 必须关联到已认证的用户身份
  • ❌ 不需要签发 access_token / refresh_token — RTC Agent Server 自己签发 JWT
  • ❌ 不需要实现标准 OAuth2 的 /token 端点 — /oauth2/token/exchange 本质是用户信息接口
  • ❌ 不需要支持 scope、PKCE 等扩展

实现好你的 OAuth2 服务后,在 Server 配置中指向它:

providers:
mock:
enabled: true
url: "https://your-oauth-server.com" # 你的 OAuth2 服务地址
client_id: "your-client-id" # 与你的服务约定的 client_id
client_secret: "your-client-secret" # 与你的服务约定的 client_secret

⚠️ providers.mockmock 是 provider 名称(不是”测试用”的意思)。Server 会将 {url}/oauth2/authorize{url}/oauth2/token/exchange 拼接为两个端点地址。如果你的服务路径不同,需要扩展 BuildProviderClients 或保持路径一致。