OpenClaw 用户认证插件,实现完整的两阶段 onboarding 流程:
- 身份绑定 — SMS 验证码将手机号与渠道身份绑定
- 准入验证 — 邀请码验证获得使用权限
整个流程在 before_dispatch hook 中完成,零 LLM 调用。只有 status = "active" 的用户消息才会放行到 LLM。
unbound → bound → active
SMS验证 邀请码验证
| 状态 | 含义 | 处理方式 |
|---|---|---|
unbound |
未绑定手机号 | 插件拦截,引导 SMS 验证 |
bound |
已绑定,未通过邀请码 | 插件拦截,引导邀请码验证 |
active |
已绑定 + 已验证 | 放行到 LLM |
src/
├── index.ts # 插件入口,注册 before_dispatch hook + 管理工具
├── types.ts # RedisLike 接口、共享类型
├── config.ts # 配置解析 + 回复话术常量
├── redis-client.ts # ioredis 连接
├── phone-utils.ts # 手机号标准化、脱敏、校验
├── binding-store.ts # 渠道绑定 CRUD (Redis)
├── invite-store.ts # 邀请码 CRUD (Redis)
├── sms-client.ts # 阿里云短信 API
├── profile-sync.ts # 同步到 profiles.json / identity-map.json
├── dispatcher.ts # 主路由:查状态 → 分发到对应 handler
└── handlers/
├── sms-handler.ts # 阶段一:SMS 验证状态机
└── invite-handler.ts # 阶段二:邀请码验证
在 plugins.json5 中添加:
"user-auth-plugin": {
enabled: true,
config: {
agents: ["personal"], // 只对 personal agent 启用认证,空数组或不配置 = 所有 agent 不走认证
redis: {
url: "${REDIS_URL}",
prefix: "clawdot:",
},
sms: {
provider: "aliyun",
endpoint: "dysmsapi.aliyuncs.com",
accessKeyId: "${ALIYUN_ACCESS_KEY_ID}",
accessKeySecret: "${ALIYUN_ACCESS_KEY_SECRET}",
signName: "南京里海数据科技",
templateCode: "SMS_504260009",
templateParamKey: "code",
},
invite: {
applyUrl: "https://clawdot.ai/invite",
},
profiles: {
dataDir: "${OPENCLAW_STATE_DIR}/identity/users",
},
rateLimit: {
smsResendIntervalSeconds: 60,
smsMaxAttemptsPerHour: 5,
smsMaxDailyTotal: 200,
inviteMaxAttemptsPerMinute: 5,
},
},
}agents 字段控制哪些 agent 需要走认证流程:
| 配置 | 行为 |
|---|---|
agents: ["personal"] |
仅 personal agent 的消息走认证,其他 agent 直接放行 |
agents: ["personal", "assistant"] |
多个 agent 走认证 |
agents: [] 或不配置 |
所有 agent 不走认证(插件完全不生效) |
Agent ID 从 ctx.agentId 获取,回退到从 sessionKey(格式 agent:{agentId}:...)解析。
仅 main agent 可用:
| 工具 | 功能 |
|---|---|
invite_create |
批量生成邀请码 |
invite_list |
列出邀请码及使用状态 |
invite_revoke |
撤销未使用的邀请码 |
npm install
npm run build
npm test详见 docs/design.md。