账号登录
手机号登录支持中国大陆手机号,可带 +86 或 0086,统一以十一位手机号保存在既有唯一字段 rg_users.username。首次验证成功即注册。邮箱验证码同样支持自动注册;目前接受常见 ASCII 邮箱,去掉首尾空白并统一小写,不支持引号或国际化邮箱格式。邮箱账号使用独立的 email: 身份命名空间,不与手机号或密码账号隐式合并。既有内部用户 ID 和服务归属保持不变。
短信验证码为六位,有效期五分钟,发送间隔六十秒,发送与尝试次数有界,成功验证后仅消费一次。客户端另外证明本机设备和签名密钥的持有权。已有账号保持原公钥;平台以 account-authority-v1 证书将各客户端自己的签名密钥关联到原账号,不上传、短信恢复或由服务端代签账号根私钥。显式短信登录后,已撤销的端点须使用新端点。注销继续要求签名、已审阅的删除计划和回执;手机号、邮箱账号分别使用对应身份的新验证码复核。
短信配置
LINKCATTLE_SMS_CONFIG_FILE 指向受保护 JSON 文件,字段为 access_key_id、access_key_secret、sign_name、template_code,可选 template_param_name 默认 code。使用 SparkData 项目已批准的短信模板及明确授权的凭据;配置过程不创建、修改或借用其他项目凭据。本机实际文件放在 ~/Documents/self/,父目录 700、文件 600;生产挂载使用同等保护的秘密目录。缺失配置返回 sms_not_configured,不会声称已发送。
共用 Kubernetes 集群中,deploy/platform-cluster.json 和 deploy/platform.yaml 直接引用既有的 sparkdata/sparkdata-notification Secret。平台读取 LINKCATTLE_SMS_ACCESS_KEY_ID、LINKCATTLE_SMS_ACCESS_KEY_SECRET、LINKCATTLE_SMS_SIGN_NAME、LINKCATTLE_SMS_TEMPLATE_CODE,依次映射 access-key-id、access-key-secret、sms-sign-name、sms-template-code;邮件另用下文的 DirectMail 环境字段,通知 webhook 不导入。LINKCATTLE_SMS_TEMPLATE_PARAM_NAME 可覆盖默认参数名 code;全部必需项均须有效。显式设置 LINKCATTLE_SMS_CONFIG_FILE 时优先读取文件,文件无效不会回退到环境凭据。Secret 变更后须重建平台 Pod 才能刷新环境变量。修改清单本身不会部署服务,也不代表真实短信发送已验证。
发送遵循阿里云官方 SendSms 与 V3 签名,固定服务地址并保持 TLS 校验。验证码与供应商凭据不返回 App、不进入日志。
接口与原生操作
平台路径以下均相对 /gateway/api:
| 接口 | 请求 | 结果 |
POST /accounts/sms/send | {phone} | {sent,retry_after,expires_in} |
POST /accounts/sms/identity | {phone,code,signing_key,proof} | 原账号身份及绑定客户端签名密钥的短时单次挑战 |
POST /accounts/sms/login | {phone,device,device_proof} | 既有会话结构与账号委托证书 |
SDK 生成持有证明。原生操作为 sms_send {phone}、sms_login {phone,code},均不返回验证码或私钥。sms_send 也用于当前手机号账号注销前的验证;既有注销请求支持 sms_code,或兼容沿用 password 字段承载验证码。
sh tests/sms-summary.sh 启动隔离 MySQL、Redis 与仅限 loopback 的合成短信替身,不发送真实短信,不调用外部模型,不迁移线上账号。真实供应商调用、手机实收与生产上线分别验证。
运行该脚本时设置 LINKCATTLE_SMS_CONNECTION_CHECK=1,还会验证真实 CLI、网站完整 URL、手机浏览会话引导、文件传输权限及短信切换账号。先构建 linkcattle-runtime、rg-client、rg-local-runtime 和 rg-endpoint-fixture;Node.js 不在 PATH 时通过 NODE_BIN 指定已有程序。
邮件配置
获准的 Kubernetes 配置只复用既有 sparkdata/sparkdata-notification Secret 的 access-key-id、access-key-secret、dm-account-name,分别映射 LINKCATTLE_EMAIL_ACCESS_KEY_ID、LINKCATTLE_EMAIL_ACCESS_KEY_SECRET、LINKCATTLE_EMAIL_FROM,并设置 LINKCATTLE_EMAIL_PROVIDER=aliyun。实现通过 TLS 和 ACS3 签名调用阿里云 SingleSendMail,验证码正文放在请求体中,不新建发件地址、不轮换凭据、不增加供应商权限。
也可用 LINKCATTLE_EMAIL_CONFIG_FILE 指向受保护 JSON,字段为 access_key_id、access_key_secret、from_address,路径及权限规则同短信。配置缺失或无效返回 email_not_configured;供应商响应不确定时不自动重试。存在发件配置不代表真实投递成功,供应商接受和真实邮箱收件须分别验证。
邮箱接口为 POST /accounts/email/send {email}、/accounts/email/identity {email,code,signing_key,proof}、/accounts/email/login {email,device,device_proof},回执与短信同形。原生操作为 email_send、email_login。账号注销接受已验证的 email 与 email_code,也兼容由既有 password 字段承载验证码;其他邮箱不能代替当前账号复核。
原生会话自动续期
CLI 登录档案使用 ~/.linkcattle/config/profiles/。这个凭据专用目录可以映射到已受保护的物理目录,普通配置与运行数据继续留在应用目录;本机目标应位于 Documents/self/。目标目录须预先具备私有权限,文件保持 600,拒绝单个档案文件的符号链接。现有目录应在 Runtime 更新切换时核对内容后迁移,保护在用连接;旧版 Runtime 可能不支持该目录映射。
原生访问会话最长 24 小时,并受续期授权的绝对期限约束。密码、短信、邮箱与浏览器登录同时支持绑定本机的续期授权(device-session-v1):闲置 90 天过期,绝对有效期最多 365 天。续期凭据仅保存在客户端受保护的凭据位置,服务端只保存摘要;续期还需原设备对用途、账号、设备、凭据摘要、一次 nonce 和最多 120 秒期限签名。不保存密码、不变更账号或电脑身份、不重新登记设备、不扩大服务权限。
POST /accounts/session/enroll 通过仍有效的访问会话和设备证明升级旧登录;已过期的旧登录需明确认证一次。/accounts/session/refresh 签发新访问会话。退出及由有效续期授权认证的 /accounts/session/revoke 会撤销该账号在当前设备上的全部登录会话和续期授权,保留其他设备登录与服务归属;已过期的授权只能撤销自身。三个用途分别签名,nonce 只消费一次;升级回执丢失后可恢复同一授权,但不能重置绝对期限。访问 Token 正常重叠至各自到期;退出、设备撤销、账号注销或停用、续期授权到期均会阻止继续访问及续期。
SDK 合并共享会话的并发续期,由现有宿主凭据保存流程持久化。Runtime 后台及恢复运行后检查,CLI 使用同一受保护档案。断网与服务端临时故障保留资料并退避;续期授权被拒绝或到期才提示重新登录。临近绝对期限或授权到期时,平台按授权去重写入账号级 authentication_required 通知;同一电脑已有更新的有效登录时,不再发送旧授权的过期提醒。入队不等于手机实收。恢复不自动重发已提交的业务操作。旧平台继续沿用原登录行为,浏览器 Cookie 与原生会话各自独立。
sh tests/session-renewal.sh 使用隔离 MySQL/Redis、合成身份和 loopback 服务,验证续期、设备签名边界、nonce 重放、旧登录升级、重启/回执丢失恢复、并发、退出撤销、账号停用注销及电脑/服务身份保持;不使用线上凭据,不代表实体休眠唤醒或手机通知实收验收。
旧登录取得续期凭据后,可能在首次保存前又刷新访问凭据。Runtime 仅在磁盘登录仍匹配原快照时保存这两项结果;并发退出、新登录或身份变化会阻止旧结果覆盖。回归使用真实 SDK 和 loopback 服务,再由新的 Runtime 测试进程读取已落盘凭据继续刷新。安装包已生成或安装,不代表正在运行的旧 Runtime 已完成切换。
系统浏览器登录
CLI 浏览器授权遵循 RFC 8252 和 PKCE S256。Runtime 持有 loopback 监听、verifier、state 和设备凭据,CLI 只打开公开授权页并查询 Runtime 状态。回调必须精确为 http://127.0.0.1:<随机端口>/callback,拒绝其他主机、路径、查询参数和低端口。授权码两分钟过期、只消费一次,并绑定原请求、回调、签名公钥和设备端点。整个流程五分钟过期,同一流程内不能替换端点。
POST /accounts/browser/start 返回 request_id、authorization_url、expires_in。浏览器完成验证后,/accounts/browser/token 使用短期授权码、verifier 和设备证明交换既有身份挑战,/accounts/browser/login 再完成通常的设备签名并返回既有设备会话结构。账号或业务私钥不交给浏览器页面。
浏览器使用独立、仅限当前主机的 Secure、HttpOnly、SameSite=Lax Cookie,不能作为服务 API 的 Bearer 凭据。默认活动后滚动保留 90 天,绝对上限 365 天;LINKCATTLE_BROWSER_SESSION_IDLE_SECONDS、LINKCATTLE_BROWSER_SESSION_ABSOLUTE_SECONDS 可配置这些边界,最短 300 秒、绝对最长 365 天。有效会话直接返回原回调,不重复验证码。退出需同源 CSRF 证明。浏览器登录、退出与过期均不启动 Agent,也不重放业务操作。token、PKCE verifier 不进入 localStorage、网址或日志;loopback URL 中只有短期授权码与 state。
sh tests/account-browser-login.sh 使用隔离 MySQL、Redis、合成验证码及正式签名原语,不发送真实邮件、短信,不注册生产 Runtime。运行前通过 LINKCATTLE_TEST_BIN_DIR 指定包含 linkcattle-platform、rg-auth-fixture 的目录。