认证、安全与设置:跨端状态的边界管理
一、认证是多层协议,不是一个 token
Folo 的认证相关代码分布在 shared、SSR client、mobile login、desktop/renderer hooks 和服务端 API 边界。依赖中能看到 better-auth、Stripe、recaptcha、secure-store、Cookie migration 等,说明它需要同时处理:
- Web Cookie/Session;
- 移动端原生安全存储;
- Electron 的本地环境;
- 社交登录、邮箱登录和 2FA;
- 付费计划/IAP/Stripe;
- 服务端公开分享与私有资源的区别。
二、认证状态应怎样流动
退出登录不能只清除一个 token。若本地数据库保留上个账号的 Feed、Entry 和订阅,切换账号后就会出现数据串号;因此认证变化应触发 Store/Query/DB 的清理或隔离。
三、SSR 的公开与私有边界
分享 Feed/List/User 页面需要匿名可读的公开投影;账号设置、私有订阅、购买和 mutation 则需要认证上下文。SSR Meta handler 能查询页面所需公开数据,并把少量 hydration 数据注入 HTML,但不能把私有会话信息写入公开页面。
审查一条新 SSR 路由时,应问:
- 这条 URL 是否可被爬虫访问?
- 服务端查询是否自动带上用户凭证?
- hydration payload 是否包含私有字段?
- 页面上的 CTA 是 deep link、登录跳转还是直接 mutation?
四、Electron 的安全边界
Electron 采用 context isolation 时,Preload 用 contextBridge 暴露能力;Renderer 不应直接取得任意 Node API。内容阅读又会处理外部 HTML,因此 Electron 安全和内容安全是叠加关系:
- 外部页面不能借由 HTML 执行任意脚本;
- Renderer 不能借由 XSS 触碰 Node;
- IPC 参数必须验证;
- 本地文件、剪贴板、协议和下载要有明确的用户动作。
五、设置系统为什么在 shared
packages/internal/shared/src/settings 有 interface、constants、defaults、hook 等文件;它们被桌面和移动端共享,但具体持久化方式可由平台决定。设置属于“用户偏好事实”,因此应提供默认值、版本迁移和类型约束。
一个好的设置项需要说明:
- 作用域是设备、账号还是单个订阅;
- 默认值是什么;
- 是否同步;
- 是否影响后台任务或网络请求;
- 变更是否需要重启/重建播放器/清理缓存。
六、支付与权限不要混成 UI 条件
桌面和移动端分别有商店渠道、IAP/Stripe、Plan 页面和升级提示。页面可以展示“需要升级”,但真正的权限判断应由服务端能力、用户计划和 client-sdk 返回的状态决定。
否则会出现“按钮隐藏了,但 API 仍然可调用”或“客户端认为已购买,服务端尚未确认”的不一致。UI 只能是权限的投影,不是权限来源。
七、删除、导出与隐私
数据库提供导出和删除入口,设置里也有 Data/Privacy 类路由。这些能力应被当成数据治理的一部分:
- 导出要说明包含哪些派生数据和聊天记录;
- 删除要区分本地数据库、服务端账号数据和缓存;
- 私密列表和收藏不能因为分享页 Meta 而泄漏;
- 日志、Sentry、分析和 AI 请求要避免把正文/凭证当作普通上下文发送。
安全设计的最后一条原则是可解释:用户和维护者都应该能回答“这份数据在哪里、由谁读、何时删除”。