邀请制账号:几处不显然的取舍

阅读约 2 分钟

注册入口只有一个

这个站不开放注册。想进来的人必须拿到我发出的邀请码,码里带着身份(协作者 / 访客)和可访问范围。

用 better-auth 做邮箱密码登录,邀请码是自己建的一张表。注册接口不直接暴露 better-auth 的 sign-up,而是包一层 /api/join:先验码,再建号,建号成功才把码标记成已用。

顺序很重要。先建号后验码的话,一旦建号成功但验码失败,就留下了一个没有邀请来源的孤儿账号。

失败锁定按账号,不按 IP

登录失败 5 次锁定 15 分钟——这个需求听起来简单,实现时有个坑:绝大多数框架自带的限流是按 IP 的共享桶。

按 IP 的问题在于,同一个公网 IP 后面可能有很多人。家里、公司、运营商 NAT 后面,一个人试错五次,其他人都跟着被锁。而且这种桶通常成功请求也计数,连续登录几次正常账号也会被误伤成 429。

所以真正的锁定自己实现:一张表按邮箱维度记账,只有 401(凭据错误)才累加,登录成功立即清零。框架自带的限流保留着,但只当粗粒度的 DoS 防护用。

GitHub 登录必须白名单

接入 GitHub OAuth 后,如果不加限制,等于给邀请制开了一个后门——任何人拿一个 GitHub 账号就能注册。

处理方式是在建号之后拦截:拿 account 表里的 accessToken 去 GitHub API 取登录名,不在白名单里就删号拒绝。

这里的原则是 fail-closed:白名单为空、取不到 token、GitHub 接口报错,任何一种情况都按"拒绝"处理。宁可让人登录不上,也不能让不该进来的人进来。

会话要能单条吊销

会话有效期 30 天是很长的一段时间,长到足够让人忘记自己在哪台设备上登录过。所以设备管理页不是可选项:能看到全部活跃会话,能一条一条吊销,也能一次性踢掉除当前之外的所有设备。

实现上没有自己造轮子,直接用 better-auth 的会话接口。但吊销要落审计日志——尤其是别人吊销你的时候。

← 上一篇家庭服务器从零到能跑