整域邮箱连接
配置 Google 服务账户和 Microsoft 应用,使工作区只需一位管理员批准,即可连接 Google Workspace 域或 Microsoft 365 组织中的所有邮箱。
工作区只需一位管理员批准,就能连接 Google Workspace 域或 Microsoft 365 组织中的全部邮箱,无需密码,也无需逐个邮箱登录。控制台中的入口分别是添加账户 > Google > 整个 Workspace 域和添加账户 > Microsoft > 整个组织,两者都是连接这类邮箱的推荐方式。客户侧操作见连接整个 Google Workspace 域和连接整个 Microsoft 365 组织。
本页介绍运维侧准备:实例需要具备哪些条件,这两个选项才能工作。在完成配置前,两者都关闭;自托管实例的控制台会将它们显示为禁用,并列出尚未设置的配置项。GET /emails/grants/config 也通过 google_missing 和 microsoft_missing 报告相同信息。
本页所有配置仅由后端读取。后端自行签发这些邮箱需要的每个令牌,并在工作节点需要时提供短期令牌,因此工作节点和消费者都不需要本页中的任何配置。
Google Workspace
Google Workspace 域通过全域委派,授权给属于你实例的 Google Cloud 服务账户。你只需创建一次服务账户,并将密钥加载到后端。之后每个客户的 Workspace 超级管理员在自己的管理控制台授权该服务账户,你这边无需按客户调整配置。
创建一次,然后长期保留
每个客户的授权都指定服务账户的客户端 ID。删除后重新创建服务账户,即使名称相同,也会得到新的客户端 ID;所有基于旧 ID 的域授权都会停止工作,直到管理员授权新 ID。项目 ID 和服务账户 ID 也都不可更改。请在第一个客户授权前选定它们,之后只轮换密钥,不要替换账户。
配置服务账户
选择项目。 在 Google Cloud 控制台中为此创建项目,或选择已有项目,例如保存 BOX_GOOGLE_* 或 GOOGLE_CLIENT_ID 应用的项目。项目 ID 是永久的,因此应选择准备长期保留的项目。
启用 API。 在 APIs & Services > Library(API 和服务 > 库) 中,为该项目启用 Gmail API 和 Admin SDK API。使用服务账户令牌发起的调用计入该项目;任一 API 未启用,Google 都会拒绝相关调用。
创建服务账户。 在 IAM & Admin > Service accounts(IAM 和管理 > 服务账户) 中选择 Create service account(创建服务账户):
- 服务账户名称:可以使用
Warmbly Workspace。 - 服务账户 ID:从名称自动填入
warmbly-workspace,此 ID 不可更改。 - 说明:可选,只有你能看到。
在 Permissions(权限) 步骤不授予任何角色,继续即可:服务账户不需要访问你的项目,只需要各客户授权的访问权限。同样跳过 Principals with access(有权访问的主体),即向用户授予此服务账户访问权限,然后选择 Done(完成)。
创建 JSON 密钥。 打开服务账户,进入 Keys(密钥) 选项卡,选择 Add key > Create new key > JSON > Create(添加密钥 > 创建新密钥 > JSON > 创建)。浏览器会下载密钥文件。Google 不保留副本,请像保护 CREDENTIALS_ENCRYPTION_KEY 一样保存它。
如果 Google 提示已禁用服务账户密钥创建,说明组织策略阻止了该操作。自 2024 年 5 月起创建的 Google Cloud 组织,默认强制执行禁止创建服务账户密钥,对应 iam.disableServiceAccountKeyCreation 或其托管形式 iam.managed.disableServiceAccountKeyCreation。拥有组织政策管理员角色的人员,可在 IAM & Admin > Organization policies(IAM 和管理 > 组织政策) 中仅针对该项目覆盖此限制:打开策略,选择 Manage policy(管理政策),覆盖父级政策并关闭强制执行。创建密钥后,如果需要继续执行限制,可以恢复策略;已创建的密钥仍能正常使用。
找到客户端 ID。 它是密钥文件中的数字 client_id,也就是控制台显示的服务账户 Unique ID(唯一 ID)。你无需手动将其复制到其他地方:加载密钥后,控制台会在整个 Workspace 域对话框中,将它与需要授权的权限范围一起显示给客户。它不是 OAuth 客户端的 ...apps.googleusercontent.com ID。
将密钥加载到后端。 仅在后端设置以下任一项:
GOOGLE_WORKSPACE_DELEGATION_KEY_FILE:后端容器内密钥文件的路径。推荐此方式,因为密钥保持为文件,可只读挂载。GOOGLE_WORKSPACE_DELEGATION_KEY:密钥的 JSON 内容本身。两者同时设置时,此项优先。
使用一条命令安装时,将文件放在安装目录旁,通过安装器不会重写的 docker-compose.override.yml 挂载到后端:
services:
backend:
volumes:
- ./secrets/google-workspace-delegation.json:/run/secrets/google-workspace-delegation.json:ro# .env
GOOGLE_WORKSPACE_DELEGATION_KEY_FILE=/run/secrets/google-workspace-delegation.json后端以 uid 1000 运行,因此文件必须对该用户可读,可执行 chown 1000 secrets/google-workspace-delegation.json && chmod 400 secrets/google-workspace-delegation.json。如果改用 .env 中的内联形式,请将 JSON 压成一行(jq -c . key.json),并用单引号包住。
重启后端并检查结果。现在应能使用添加账户 > Google > 整个 Workspace 域,GET /emails/grants/config 返回 google_enabled: true 和服务账户的 google_client_id。如果提供的值不是服务账户密钥,启动时会记录一条日志,该选项保持关闭。管理面板在实例 > 配置 > 环境中展示这两个设置,显示已设置或未设置及简短指纹,绝不显示实际值。
证明域名控制权
客户端 ID 属于你的实例,不属于某个工作区,因此客户授权本身并不能说明由哪个工作区发起。Google 授权只会记录到能够证明控制该域的工作区,并且域目录必须将管理员地址列为超级管理员。证明方式有两种:
- 管理员通过 Google 登录。 使用控制台自己的 Google 登录客户端(
GOOGLE_CLIENT_ID、GOOGLE_CLIENT_SECRET),返回现有重定向 URI,即GOOGLE_REDIRECT_URI,或API_PUBLIC_URL加/v1/auth/google/callback,只请求openid和email。回调识别管理员证明后,将其交回打开流程的控制台窗口,不会登录任何用户。除了现有登录所用 URI,无需再注册重定向地址。未配置该客户端时,邮箱客户端BOX_GOOGLE_*会以相同方式使用自己的重定向 URI/addresses/google/callback,同样只请求openid和email。登录包含 PKCE 挑战和 OpenID nonce;后端用匹配的验证器兑换授权码,只有 ID 令牌回传相同 nonce 才接受。 - DNS 记录。 设置一条
TXT记录,位置为_warmbly.<domain>,内容为对工作区和域唯一的warmbly-verify=值,以AUTH_SECRET为密钥生成。此方式始终提供;如果实例没有配置任何 Google 客户端,它就是唯一证明方式。
客户管理员会从自己的组织登录该客户端,因此 OAuth 同意屏幕必须为 External(外部)。 Internal(内部) 同意屏幕只允许你自己的 Google 组织账户,其他管理员会被拒绝,只能使用 DNS 记录。
密钥保管与轮换
该密钥可以为任何已授权其客户端 ID 的域中的任何地址签发令牌。它是实例上所有此类邮箱背后的统一凭据,因此只能保存在后端:工作节点每次使用时只接收短期访问令牌,绝不接触密钥;系统也不存储这些邮箱的令牌。
轮换密钥的步骤:
- 在服务账户的 Keys(密钥) 选项卡中创建新的 JSON 密钥。
- 替换文件或内联值,并重启后端。
- 确认授权仍能通过,例如对其中一项选择重新检查,或调用
POST /emails/grants/:id/check。 - 在 Keys(密钥) 选项卡删除旧密钥。
客户无需改动:他们授权的客户端 ID 属于服务账户,不属于密钥。
Google 应用验证
Google 将全域委派视为客户管理员在自己管理控制台中批准的授权。这一路径没有需要 Google 审核的 OAuth 同意屏幕,因此服务账户无需针对 Gmail 权限范围进行 Google 应用验证,也不需要 CASA 安全评估。Google 规则可能变化,在依赖这一点之前,请根据 Google 当前关于验证要求例外的帮助页面确认。域证明使用的登录客户端只请求 openid 和 email,两者不是敏感权限范围。
逐邮箱的使用 Google 登录路径,因其受限 Gmail 权限范围而需要 Google 验证和安全评估。该路径使用 BOX_GOOGLE_* 客户端,目前正逐步由整域连接和应用专用密码替代。参见迁移现有邮箱。
客户需要执行的操作
-
Workspace 的超级管理员打开 admin.google.com,进入 Security > Access and data control > API controls > Manage domain-wide delegation(安全 > 访问和数据控制 > API 控制 > 管理全域委派)。
-
选择 Add new(新增),粘贴控制台显示的客户端 ID,并准确输入以下三个权限范围,以逗号分隔:
https://www.googleapis.com/auth/gmail.modify,https://www.googleapis.com/auth/gmail.settings.basic,https://www.googleapis.com/auth/admin.directory.user.readonly -
选择 Authorize(授权),等待几分钟;管理控制台需要一些时间应用变更。
-
在 Warmbly 中通过管理员登录或 DNS 记录证明域所有权,再选择要连接的邮箱。
应先授权再证明,因为 Warmbly 会立即检查授权。完整流程见连接整个 Google Workspace 域。
Google 拒绝请求时
google_delegation_unauthorized 表示 Google 未接受委派,或接受后拒绝了某次调用。按常见出现顺序检查:
- 尚未生效。 管理控制台变更通常在几分钟内生效,偶尔更久。等待后重试,或使用重新检查。
- ID 错误。 填入的是 OAuth 客户端 ID(
...apps.googleusercontent.com),而不是服务账户的数字客户端 ID。 - 权限范围不同。 条目必须准确包含上面的全部三个权限范围。缺少一个、拼写错误,或用其他 Gmail 权限代替,都会被拒绝。
- 不是超级管理员。 Warmbly 中输入的地址不是该域的超级管理员,因此无法使用它读取目录。
- API 未启用。 你的服务账户所在项目未启用 Gmail API 或 Admin SDK API;这是你这边的配置,不是客户那边。
曾经有效、后来每小时检查失败的授权会显示为无效,其邮箱会停止;检查再次通过后,邮箱自动恢复。参见 google_delegation_unauthorized。
Microsoft 365
Microsoft 365 组织通过管理员同意,授权给实例现有的 Outlook 应用,即逐邮箱 Microsoft 登录使用的 BOX_OUTLOOK_CLIENT_ID 和 BOX_OUTLOOK_CLIENT_SECRET。无需第二个应用,也没有新设置;只要这两个值已设置,该选项就会启用。应用注册需要补充三项配置。
配置应用注册
打开现有注册。 在 Microsoft Entra 管理中心进入 App registrations(应用注册),打开 ID 与 BOX_OUTLOOK_CLIENT_ID 对应的应用。如果尚未创建,请先按 Outlook 和 Microsoft 365 操作。
设为多租户。 在 Authentication(身份验证) 中,将 Supported account types(支持的账户类型) 设为“任何组织目录中的账户(多租户)”,让其他组织能够同意。还包含个人 Microsoft 账户的选项也是多租户;Outlook.com 邮箱进行逐邮箱登录时需要这一选项。
保留重定向 URI。 现有 Web 重定向 URI,即 API 基础地址加 /addresses/outlook/callback,也用于管理员同意后的返回,无需新增。
添加应用程序权限。 在 API permissions(API 权限) 中选择 Add a permission > Microsoft Graph > Application permissions(添加权限 > Microsoft Graph > 应用程序权限),添加 Mail.ReadWrite、Mail.Send 和 User.Read.All。它们与逐邮箱登录现有的委派权限 User.Read、Mail.Send、Mail.ReadWrite 和 offline_access 并列,请保留原权限。之后列表中 Mail.ReadWrite 和 Mail.Send 各出现两次,一次类型为 Delegated(委派),一次为 Application(应用程序),这是正确的。
不要为自己的租户授予管理员同意,除非正在用它测试。页面上的 Grant admin consent(授予管理员同意) 按钮只会代表你的组织同意。每个客户组织通过控制台为自己授权。
条件允许时验证发布者。 没有验证也能使用管理员同意。验证会在同意屏幕添加已验证徽章,有些管理员会检查它;同时,它也是多数组织的普通用户能够自行批准登录单个邮箱的条件:对于新建、未验证且请求邮件权限的多租户应用,Microsoft 会把所有用户都转交管理员处理。参见发布者验证。
仅修改应用注册无需重启。GET /emails/grants/config 返回 microsoft_enabled: true 的条件是两个 BOX_OUTLOOK_* 值都已设置。
应用程序权限可以访问同意组织中的全部邮箱。建议每个组织通过 Exchange Online 应用程序访问策略,或应用程序 RBAC,将应用限定到 Warmbly 应使用的邮箱;策略范围外的邮箱会在连接时被拒绝。命令见连接整个 Microsoft 365 组织。
客户需要执行的操作
组织的全局管理员选择添加账户 > Microsoft > 整个组织,在 Microsoft 管理员同意页面选择一次 Accept(接受)。随后窗口会让管理员登录,通常无需再次询问,使 Warmbly 知道由谁批准。该登录携带 PKCE 挑战和 OpenID nonce,检查方式与 Google 相同。记录的组织始终采用 Microsoft 报告的登录所属组织,绝不采用请求中的值;只有该登录在组织中具有全局管理员或特权角色管理员角色,才会记录授权。角色从登录的 Microsoft Graph 令牌读取,该令牌默认列出目录角色;如果应用注册在 ID 令牌中发出角色,也可从那里读取。之后成员选择要连接的邮箱。
Microsoft 拒绝请求时
microsoft_consent_missing 表示 Microsoft 没有为组织签发令牌,或签发后拒绝了调用:
- 尚未同意。 组织未批准应用,或在选择接受之前关闭了同意页面。请让全局管理员重新连接。
- 不是管理员。 为应用程序权限授予管理员同意需要全局管理员,普通用户登录不能授予。
- 权限缺失。 应用注册缺少三个应用程序权限之一,或这些权限是在组织同意之后添加的。同意只覆盖当时应用已有权限,因此修改后管理员必须再次同意。
- 单租户。 应用注册不是多租户,其他组织无法找到它。
- 未知组织。 Microsoft 不认识同意过程中指定的租户。
Microsoft 同意流程返回 mailbox_grant_proof_missing,表示登录者在该组织中没有全局管理员或特权角色管理员角色。请使用具有相应角色的账户登录。
迁移现有邮箱
逐邮箱的使用 Google 登录正在逐步退出。以此方式连接的邮箱仍会继续工作,目前没有停用日期;控制台会标记它们,并提供逐个迁移功能,不会丢失历史、营销活动或预热:
- Google Workspace 邮箱迁移到域管理员授权。通过该授权连接其地址,会原地转换现有邮箱。
- 个人 Gmail 邮箱或任何 Google 邮箱,可以改用应用专用密码;系统会先向 Gmail 验证,再存储任何信息。
逐邮箱 Microsoft 登录不会退出,但也可以用相同方式迁移到组织授权。客户侧说明见迁移离开逐邮箱 Google 登录。只要还有邮箱使用旧方式,就应保留 BOX_GOOGLE_* 客户端配置,因为它负责刷新这些邮箱的令牌。