# 服主配置指南 本文面向部署 `bd2server` 的服主,不面向普通玩家。 此文为极其诡异不说人话的 GPT 系列模型编写,所以如果你看不懂,那么请丢给 AI。 为避免混淆,本文统一使用以下名称: - `provider access token`、`provider ID token`:Discord/Google 在 OAuth/OIDC 流程中返回的凭据,仅由服务端临时使用。 - `game access token`、`game refresh credential`:`bd2server` 自己签发、供玩家客户端访问本服使用的凭据。 除非明确写出 `provider`,下文提到的 access token 或 refresh credential 都指 `bd2server` 自己签发的 game credential。 客户端会受到配置结果影响,例如显示哪些按钮、登录能否成功,但客户端不保存或管理 `client_secret`、master key。 也就是说,即使你服务器上什么都有,那他们的客户端 `GET /auth/config` 只能看到: ```json { "mode": "oauth", "providers": ["discord", "google"] } ``` 玩家点击第三方登录后,服务端会为本次登录生成一次性随机 `state`、PKCE verifier/challenge,以及 Google OIDC `nonce`。Provider callback 必须匹配对应的短期 login transaction;`state`、授权码和登录事务不能跨事务复用。服主不需要手工生成或配置这些值。 ## 1. 准备公网地址 联机服务器需要一个长期稳定、玩家和 provider 都能访问的 HTTPS origin(当然,我们都自部署服务器了,公网地址肯定是有的),例如: ```text https://bd2.example.com ``` 要求: - `public_url` 只能包含 scheme、主机和可选端口,不能带路径、查询参数、片段或用户信息。 - **bd2server 自身要求**公网 `public_url` 使用 HTTPS;只有 `localhost`、`127.0.0.1` 或 `::1` 开发环境允许 HTTP。Provider 还可能施加额外的 redirect URI 限制,最终 URI 也必须通过 Discord/Google 后台的校验。 - 反向代理或内网穿透必须把 `/auth/`、`/game/` 和 `/assets/` 全部转发到我们启动的 `bd2server`。 - 不要让代理缓存 `/auth/` 响应。 - 域名变化后,必须同时修改 `authentication.json` 和 provider 后台的 redirect URI,然后重启服务端,并告知玩家。 固定回调地址为: ```text Discord: /auth/discord/callback Google: /auth/google/callback ``` 例如: ```text https://bd2.example.com/auth/discord/callback https://bd2.example.com/auth/google/callback ``` ## 2. 创建 Discord OAuth 应用 1. 登录 。 2. 选择 **New App**,为自己的服务器创建一个应用。 3. 在应用的 **OAuth2** 页面复制 **Client ID**。 4. 创建应用后立即安全保存 **Client Secret**。如果后台已不显示 secret,再使用 Discord 提供的重置/轮换功能生成新 secret,并同步更新服务端环境变量。不要为了“查看”而无意义地重置仍在使用的 secret。 5. 在 **Redirects** 中添加: ```text https://bd2.example.com/auth/discord/callback ``` 6. 保存设置。 若重置 Discord client secret,旧 secret 会立即失效;更新服务器环境变量并重启即可,玩家客户端不需要改变。Authorization Code 流程使用的一次性 `state` 和 PKCE 由 `bd2server` 自动生成并在 callback 时校验。 ## 3. 创建 Google OAuth 应用 Google 当前主要在 **Google Auth Platform → Branding / Audience / Data Access / Clients** 中管理 OAuth。控制台名称以后可能调整,但需要完成的对象不变:配置应用展示与受众,并创建一个 **Web application** 类型的 OAuth client。 1. 登录 并选择或创建项目。 2. 在 **Branding** 中配置应用名称、支持邮箱等公开信息。 3. 在 **Audience** 中选择实际目标受众并确认 Publishing status: - **测试阶段**:保持 Testing,并把实际参加登录测试的 Google 账号加入 Test users。未列入的玩家会被 Google 拒绝。 - **向玩家开放前**:检查 Audience、Publishing status 和 Google 显示的发布要求,并按实际受众切换到 In production。 4. 在 **Data Access** 中核对 scope。`bd2server` 当前只申请最小的 `openid`,不申请 `profile` 或邮箱 scope。 5. 在 **Clients** 中选择 **Create Client**,应用类型选择 **Web application**。不要创建 Desktop application 类型的 client:浏览器回调落在服务端 HTTPS endpoint,需要 Web application client。 6. 在 **Authorized redirect URIs** 中添加: ```text https://bd2.example.com/auth/google/callback ``` 7. 创建后立即安全保存 Client ID 和 Client Secret。如果 Google 后台不再允许查看或下载原 secret,就按后台提供的轮换流程创建新 secret,并同步更新服务端环境变量。 ## 4. 生成并保存 master key 每台服务器生成一次 32-byte 随机 master key。 Windows(PowerShell 7): ```powershell $keyBytes = [byte[]]::new(32) $rng = [Security.Cryptography.RandomNumberGenerator]::Create() try { $rng.GetBytes($keyBytes) [Convert]::ToBase64String($keyBytes) } finally { $rng.Dispose() [Array]::Clear($keyBytes, 0, $keyBytes.Length) } ``` macOS/Linux(Bash 或 Zsh): ```bash master_key="$(openssl rand -base64 32)" printf '%s\n' "$master_key" unset master_key ``` 把输出保存进密码管理器或服务部署系统的 secret store。不要提交到 Git,也不要写进 `authentication.json`。 master key 必须长期稳定: - 重启服务端时继续使用同一个值。 - 备份 `auth.db` 时也要单独安全备份 master key。 - master key 会派生两类用途隔离密钥:HMAC 密钥用于 provider identity、game token、登录事务 secret、OAuth `state` 和客户端地址的稳定索引;AES-256-GCM 密钥用于 PKCE verifier、OIDC `nonce` 和待领取登录结果。 - 丢失或随意更换 master key 后,已有 HMAC 索引无法再匹配,已有 AES-GCM 密文也无法解密;现有 `auth.db` 认证状态不能继续使用。 - 不要在两台互不信任的服务器之间共用 master key。 ## 5. 编辑服务器配置 首次运行时会在 `bd2server` 可执行文件旁生成 `local` 模式的默认配置。开发入口使用 `.build/config/authentication.json`;Docker 镜像默认使用 `/app/data/config/authentication.json`。启用 OAuth 前编辑生成的文件并配置环境变量,然后重启。Discord 和 Google 都启用时: ```json { "mode": "oauth", "public_url": "https://bd2.example.com", "master_key_env": "BD2_AUTH_MASTER_KEY", "providers": { "discord": { "client_id": "123456789012345678", "client_secret_env": "BD2_DISCORD_CLIENT_SECRET" }, "google": { "client_id": "example.apps.googleusercontent.com", "client_secret_env": "BD2_GOOGLE_CLIENT_SECRET" } }, "session": { "access_ttl": "15m", "refresh_ttl": "720h", "device_transaction_ttl": "10m" } } ``` 配置字段 `device_transaction_ttl` 以及服务端日志中可能出现的 login transaction,指 `bd2server` 自己用于“游戏客户端发起登录 → 浏览器完成 Authorization Code → 游戏客户端领取结果”的短期交接机制。它不是 Discord 或 Google 的 OAuth Device Authorization Grant。 只启用一个 provider 时,删除另一个 provider 对象即可。`providers` 是对象映射,不是字符串数组。 JSON 中可以保存 `client_id`,因为它不是秘密;只能保存 secret 所在环境变量的名称,不能保存 secret 实值。 ## 6. 向服务端进程注入 secret 临时测试时,在启动 `bd2server` 的同一个 shell 中设置。 Windows(PowerShell 7): ```powershell $env:BD2_AUTH_MASTER_KEY = '' $env:BD2_DISCORD_CLIENT_SECRET = '' $env:BD2_GOOGLE_CLIENT_SECRET = '' .\bd2server.exe serve ``` macOS/Linux(Bash 或 Zsh): ```bash export BD2_AUTH_MASTER_KEY='' export BD2_DISCORD_CLIENT_SECRET='' export BD2_GOOGLE_CLIENT_SECRET='' ./bd2server serve ``` 如果只启用 Discord,就不需要设置 Google secret;反之亦然。服务端在 OAuth 配置或任何必需环境变量缺失时会拒绝启动。 ## 7. 启动后检查 先检查服务健康。 Windows(PowerShell 7): ```powershell $publicUrl = 'https://bd2.example.com' (Invoke-WebRequest -Uri "$publicUrl/healthz").Content ``` macOS/Linux(Bash 或 Zsh): ```bash PUBLIC_URL='https://bd2.example.com' curl --fail --silent --show-error "$PUBLIC_URL/healthz" ``` 预期输出: ```text ok ``` 再检查公开认证策略。 Windows(PowerShell 7): ```powershell Invoke-RestMethod -Uri "$publicUrl/auth/config" | ConvertTo-Json ``` macOS/Linux(Bash 或 Zsh): ```bash curl --fail --silent --show-error "$PUBLIC_URL/auth/config" ``` 正常响应只应包含公开认证策略,例如 `mode` 和 provider 名称: ```json { "mode": "oauth", "providers": ["discord", "google"] } ``` 最后再启动玩家客户端。首次进入应显示服主启用的 Discord/Google 按钮;浏览器地址应属于 Discord/Google,回调应返回配置的 `public_url`。 ## 9. 常见问题 ### 客户端需要填写 client ID 或 secret 吗? 不需要。客户端只读取服务端公开的 provider 列表。Client ID 由服务端构造 OAuth 授权 URL 时使用;client secret 只参与服务端与 provider token endpoint 之间的通信。浏览器中的 provider 授权 URL 会包含 client ID,这是正常行为,因为 client ID 本来就不是 secret。 ### client ID 写错会破坏玩家存档吗? 这些配置错误通常不会修改 `state.db`,但可能导致认证失败、callback 无法完成、请求被送到错误 host,或 login transaction 超时。修正配置并重启服务端即可;客户端不会因此得到 client secret。 ### 可以让所有服主共用项目作者的一组 client ID/secret 吗? 不建议,也不应分发共享 client secret。每位服主应创建自己的 OAuth 应用,独立控制回调域名、配额、停用和审计。共享应用会把所有服务器的信任、限流和泄漏风险绑在一起。 ### 换域名需要重新编译客户端插件吗? 登录插件本身不需要重新编译,但客户端最初连接的服务端入口、`authentication.json` 的 `public_url`、反向代理以及 provider redirect URI 必须全部指向新地址并保持一致。 ### 可以把服务直接暴露在公网 HTTP 上吗? 不可以。公网 OAuth 会传输一次性 device secret、登录结果和游戏凭据,必须由 HTTPS 保护。只有同机 loopback 开发测试允许 HTTP。 ### 如何撤销当前客户端自动登录? 客户端退出账号时会删除当前服务器 origin 对应的安全 refresh credential。服主还可以调用服务端 session revoke 流程;不要通过删除 `state.db` 来“清登录”,玩家存档与认证数据是分开的。 ### 如何重置服务器 owner? 这属于破坏性管理操作。当前没有公开的 owner 重置 API,不要手工改 SQLite。应先停止服务、完整备份并校验 `state.db`、`auth.db` 和 master key,再使用未来提供的受控管理工具。删除 `state.db` 会删除玩家存档,不是正确的认证重置方法。 ## 10. 官方教程 - Discord OAuth2: - Google OAuth 2.0 for Web Server Applications: