登录与注册配置

配 Email 验证码登录、Google OAuth、注册白名单和本地测试验证码。

NSTACK Agents 支持两种登录方式:Email + 验证码(默认)和 Google OAuth(可选)。登录成功后 server 签发一个 30 天有效期的 JWT cookie。这一页讲怎么配、怎么限制谁能注册、以及本地测试验证码怎么安全使用。

上面用到的环境变量的清单见 环境变量;token 怎么用、生命周期细节见 认证与令牌

Email + 验证码登录怎么工作

用户在登录页输邮箱 → server 发 6 位验证码 → 用户填回 → server 验证 → 签发 JWT cookie。是标准流程。支持两种邮件发送通道,按部署环境二选一:

Option A:Resend(公网/云端部署推荐)

  1. Resend 建账号、验证你的域名

  2. 创建 API key

  3. 设环境变量:

    RESEND_API_KEY=re_xxxxxxxxxxxxxxxx
    RESEND_FROM_EMAIL=noreply@yourdomain.com  # 必须是 Resend 已验证的域名
  4. 重启 server

Option B:SMTP relay(内网/自部署)

适合内网无法访问 api.resend.com,或者已经有内部邮件中继(Exchange、Postfix、自部署 SendGrid 等)的场景。同时设置时 SMTP_HOST 优先级高于 RESEND_API_KEY

SMTP_HOST=smtp.internal.example.com
SMTP_PORT=587                  # 默认 25;STARTTLS 提交端口用 587
SMTP_USERNAME=concierto          # 留空则使用未认证 relay
SMTP_PASSWORD=...
SMTP_TLS_INSECURE=false        # 仅在私有 CA / 自签证书时改成 true
RESEND_FROM_EMAIL=noreply@yourdomain.com  # 同时作为 SMTP From: 头

服务端 advertise STARTTLS 时会自动升级。暂不支持 465(SMTPS / 隐式 TLS),请使用 25 或 587。

两种都不配:server 不报错,但所有本该发出去的邮件只打到 server 的 stdout。本地开发方便(你从日志抄验证码),生产环境等于黑洞。

固定本地测试验证码

不要在公网可访问实例上启用固定验证码。

旧版「非 production 默认接受 888888」的行为已经移除。除非你显式配置,否则输入 888888 会和普通错误验证码一样被拒绝。

没配任何邮件后端(Resend 和 SMTP 都没设)的本地开发,应使用 server 日志里打印的随机验证码。如果你需要确定性的本地/私有自动化测试,可以把 CONCIERTO_DEV_VERIFICATION_CODE 设成一个 6 位数字,比如 888888,并保持 APP_ENV 为非 production:

APP_ENV=development
CONCIERTO_DEV_VERIFICATION_CODE=888888

设置固定验证码后,它是真正的快捷通道:可以直接调 /auth/verify-code 而无需先请求验证码,同一邮箱也可以反复使用而不会耗尽。APP_ENV=production 时这个快捷码会被忽略。

生产部署应保持 CONCIERTO_DEV_VERIFICATION_CODE 为空,并设置 APP_ENV=production。如果你用 make selfhost / docker-compose.selfhost.yml 自部署,APP_ENV 默认就是 production

怎么配 Google OAuth

可选。不配就只有 Email + 验证码登录;配了后登录页会多出「用 Google 登录」按钮。

  1. Google Cloud Console 创建一个 OAuth 2.0 client

  2. 授权的回调 URI(Authorized redirect URIs)填你的 NSTACK Agents 前端地址加 /auth/callback,例如:

    https://nstack.yourdomain.com/auth/callback
  3. 拿到 client ID 和 client secret 后设三个环境变量:

    GOOGLE_CLIENT_ID=xxxxx.apps.googleusercontent.com
    GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxx
    GOOGLE_REDIRECT_URI=https://nstack.yourdomain.com/auth/callback
  4. 重启 server。

热生效:前端通过 /api/config 运行时读这些配置——改完只要重启 server,前端不用重建镜像、不用重新部署。

回调 URI 在 Google Console 和 GOOGLE_REDIRECT_URI 两处必须完全一致,包括协议(http vs https)、尾部斜杠、端口。不一致 Google 会拒绝整个 OAuth 流程,用户看到的错误是 redirect_uri_mismatch

怎么限制谁能注册

三层环境变量按优先级组合:

Rendering diagram…

已经登录过的老用户永远可以再次登录——signup 白名单只对首次注册生效,不拦截老用户。

  • ALLOWED_EMAILS(最高优先级)—— 显式邮箱白名单,逗号分隔。非空时只有列表里的邮箱能注册
  • ALLOWED_EMAIL_DOMAINS—— 域名白名单,逗号分隔(例如 company.io,partner.com)。
  • ALLOW_SIGNUP —— 总开关,默认 true。设 false 完全关闭注册。

三层白名单是 AND 语义,不是 OR。 很多人第一直觉是「设 ALLOWED_EMAIL_DOMAINS=company.io + ALLOW_SIGNUP=true 就是允许 company.io 和其他所有人」——不是。任何一层白名单只要设了非空值,不匹配的邮箱直接拒ALLOW_SIGNUP=true 挡不住。

要真的「允许所有人」,所有三个环境变量都留空(或 ALLOW_SIGNUP=true)。

典型配法

需求配置
公司内网,只允许 company.io 员工ALLOWED_EMAIL_DOMAINS=company.io
公司内网 + 几个外部合作者ALLOWED_EMAIL_DOMAINS=company.io + 合作者个人邮箱加到 ALLOWED_EMAILS
完全关闭自助注册,只能邀请ALLOW_SIGNUP=false
开放注册(不推荐生产用)三个都留空

关了注册还能邀请人进来吗

只对已经有 NSTACK Agents 账号的人能。接受邀请那一步不检查 signup 白名单——如果对方已经注册过(比如在别的工作区),他们点链接登录就能直接接受。

但还没注册过的人,邀请救不了他们。他们接受邀请前必须先登录,登录的第一步(发验证码)会过 signup 白名单检查。如果你 ALLOW_SIGNUP=false、或他们的邮箱不在 ALLOWED_EMAILS / ALLOWED_EMAIL_DOMAINS 里,他们没法完成注册,也就没法接受邀请。

要邀请一个还没注册的外部协作者:临时把他们的邮箱加到 ALLOWED_EMAILS,等他们注册 + 接受邀请之后再把这条移掉。

邀请的创建和使用见 成员与权限

下一步

  • 环境变量 —— 这一页用到的环境变量完整定义
  • 认证与令牌 —— JWT / PAT / Daemon Token 的分类和使用
  • 故障排查 —— 验证码收不到、OAuth 报 redirect_uri_mismatch、注册被拒的常见排查