外观
认证与 Token
宿主先登录自己的账号系统,再通过 authProvider 为当前用户取得 Popo IM 凭证。SDK 使用这份凭证访问 IM 接口;宿主密码、登录 Cookie 与 SDK Token 分别管理。
认证链路
text
用户登录宿主应用
→ SDK 调用 authProvider
→ App / 浏览器请求宿主自己的认证接口
→ 宿主后端依据已验证登录态确定当前用户
→ 宿主后端请求 Popo IM POST /app/sdk/token
→ Provider 返回 user、token、expireAt
→ SDK 完成 /app/sdk/sync 并建立实时连接authProvider 不接收需要签票的用户 ID。宿主后端应从自己的 Session 或 Access Token 确定账号,不能直接使用客户端传入的 user_id 为任意用户签票。
两种凭证,不同调用方
| 凭证 | 保存位置 | 用途 |
|---|---|---|
| 宿主登录凭证 | 宿主自己的登录机制 | 访问宿主的账号与取票接口 |
sdkSecret | 宿主后端受控配置 | 调用 Popo IM /app/sdk/token 等服务端集成接口 |
SDK token | SDK 运行时 | 以 Bearer 访问 /app/sdk/* 与实时鉴权 |
sdkSecret 不能出现在 App、浏览器脚本、URL 或普通日志中。SDK Token 由 Popo IM 服务端签发,宿主客户端无需实现 JWT 签名。
使用内置宿主账号系统
配套应用的 POST /app/auth/token 根据已验证的宿主登录态返回 IM 凭证。接入已有业务系统时,应建立自己的取票接口,由后端调用 /app/sdk/token;不要把这两个接口的认证方式混用。
宿主后端取票
以下请求仅在宿主后端执行。user_id、昵称及头像来自该后端确认的当前用户:
http
POST https://example.com/app/sdk/token
Authorization: Bearer <sdkSecret>
Content-Type: application/json
{
"user_id": "chenyue",
"username": "chenyue",
"nickname": "陈悦",
"avatar": "https://example.com/media/chenyue.webp",
"signature": "记录每一次出发"
}成功响应的主要字段如下;为便于阅读省略宿主推送配置 push。id 是 Popo 内部用户 ID,SDK 身份采用 user_id:
json
{
"code": 0,
"msg": "ok",
"data": {
"user": {
"id": "18",
"user_id": "chenyue",
"username": "chenyue",
"nickname": "陈悦",
"avatar": "https://example.com/media/chenyue.webp",
"signature": "记录每一次出发",
"profile_updated_at": 1792296000000
},
"token": "<服务端签发的 JWT>",
"expire_at": 1794888000
}
}上面的 ID 与时间为结构示意。实际 push 是服务端按应用及平台生成的宿主推送初始化配置,不参与 SDK 的 AuthInfo。如需匹配原生推送规则,取票请求的 platform、manufacturer、package_name 必须三项一起传入;Web 接入不必提供。
| 请求字段 | 必填 | 约束 |
|---|---|---|
user_id | 是 | 1–64 字符,不能有首尾空白或控制字符;宿主稳定用户 ID |
nickname | 是 | 1–80 字符,不能有首尾空白或控制字符 |
username | 否 | 公开账号,非空时 1–64 字符;用于精确查找与好友二维码 |
avatar | 否 | 最多 1024 字符;空值使用服务端默认头像 |
signature | 否 | 最多 255 字符 |
资料字段均不能包含控制字符。取票成功时会同步该用户的公开资料,因此宿主应使用自己当前已确认的资料。
宿主接口应校验 HTTP 状态和 code,只在 code === 0 且 data 完整时返回认证结果,并设置 Cache-Control: no-store。失败时抛出可识别的宿主错误,不要返回空 Token 或沿用另一账号的凭证。
Provider 返回什么
JavaScript 两端的标准形态为:
js
return {
user: {
userId: data.user.user_id,
username: data.user.username,
nickname: data.user.nickname,
avatar: data.user.avatar,
signature: data.user.signature
},
token: data.token,
expireAt: data.expire_at
}| 平台 | Provider 形式 | 返回模型 |
|---|---|---|
| Web | () => Promise<IMAuth> | { user, token, expireAt } |
| Android | PopoAuthProvider.fetchAuth(callback) | callback.onSuccess(PopoAuthInfo) 或 onError(Throwable) |
| iOS | @Sendable () async throws -> PopoAuthInfo | PopoAuthInfo(user:token:expireAt:) |
| HarmonyOS | () => Promise<PopoAuthInfo> | new PopoAuthInfo(user, token, expireAt) |
| uni-app | () => PopoAuthInfo | PromiseLike<PopoAuthInfo> | { user, token, expireAt } |
expireAt 始终是 Unix 秒,不是 JavaScript 毫秒。SDK 会核对 JWT 中的 user_id、exp 与返回对象一致,这属于防串号与格式检查;服务端仍负责最终验签。
登录、续期与账号切换
SDK 初始化后,宿主确认自身登录态,再调用 login()。SDK 内部合并续期并处理授权错误,宿主不需要对每个聊天操作再包一层刷新 Token 或重复重试。
用户在宿主主动切换账号后,先让 Provider 的身份来源切到新账号,再显式调用 login()。各端显式登录均可切换身份;自动续期必须保持原账号,如果 Provider 在续期时返回另一用户,会停止旧会话并报告身份变化。宿主也可以先 logout() 再登录,便于同步收起旧账号界面。
用户退出宿主时同时调用 SDK logout(),再清理宿主登录态。组件或页面卸载只移除自身监听;Web 实例销毁另调用 destroy()。不要因每次切换标签而重复初始化或退出进程级 SDK。
公开资料同步
宿主保存昵称、头像或签名成功后,先更新自己的认证来源及后端取票缓存,再通知 SDK:
| 平台 | 同步入口 |
|---|---|
| Web | client.refreshCurrentUser(user);只更新本地展示,宿主后端应已完成服务端资料同步 |
| Android / iOS / HarmonyOS / uni-app | updateCurrentUser;在当前登录身份下提交变化的公开资料 |
用户 ID 不能通过资料更新修改。服务端取票缓存若未随资料版本失效,下次续期可能用旧昵称或头像覆盖新资料。
出错时如何处理
临时断网或服务不可用时保留已有页面和可读缓存,允许用户重试。宿主登录态确认失效时,清理 SDK 会话并回到宿主登录页。分支应依据平台错误码或 HTTP 状态,避免通过错误文案判断。
Token、Secret 和宿主密码不写入埋点或错误截图。排查问题优先保留操作、平台版本、业务错误码与请求跟踪 ID。
