登录与注册配置
配 Email 验证码登录、Google OAuth、注册白名单和本地测试验证码。
NSTACK Agents 支持两种登录方式:Email + 验证码(默认)和 Google OAuth(可选)。登录成功后 server 签发一个 30 天有效期的 JWT cookie。这一页讲怎么配、怎么限制谁能注册、以及本地测试验证码怎么安全使用。
上面用到的环境变量的清单见 环境变量;token 怎么用、生命周期细节见 认证与令牌。
Email + 验证码登录怎么工作
用户在登录页输邮箱 → server 发 6 位验证码 → 用户填回 → server 验证 → 签发 JWT cookie。是标准流程。支持两种邮件发送通道,按部署环境二选一:
Option A:Resend(公网/云端部署推荐)
-
在 Resend 建账号、验证你的域名
-
创建 API key
-
设环境变量:
RESEND_API_KEY=re_xxxxxxxxxxxxxxxx RESEND_FROM_EMAIL=noreply@yourdomain.com # 必须是 Resend 已验证的域名 -
重启 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 登录」按钮。
-
去 Google Cloud Console 创建一个 OAuth 2.0 client
-
授权的回调 URI(Authorized redirect URIs)填你的 NSTACK Agents 前端地址加
/auth/callback,例如:https://nstack.yourdomain.com/auth/callback -
拿到 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 -
重启 server。
热生效:前端通过 /api/config 运行时读这些配置——改完只要重启 server,前端不用重建镜像、不用重新部署。
回调 URI 在 Google Console 和 GOOGLE_REDIRECT_URI 两处必须完全一致,包括协议(http vs https)、尾部斜杠、端口。不一致 Google 会拒绝整个 OAuth 流程,用户看到的错误是 redirect_uri_mismatch。
怎么限制谁能注册
三层环境变量按优先级组合:
已经登录过的老用户永远可以再次登录——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,等他们注册 + 接受邀请之后再把这条移掉。
邀请的创建和使用见 成员与权限。