---
author: OOMOL
author_url: https://oomol.com/zh-cn/about/
title: 使用自己的域名接收 OAuth 回调
description: 为 Connector for SaaS 配置自定义 OAuth 回调地址，在自己的域名接收授权结果，再通过 302 交给 Connector 完成连接。
lang: zh-CN
canonical_url: https://oomol.com/zh-cn/docs/saas-oauth-callback/
markdown_url: https://oomol.com/zh-cn/docs/saas-oauth-callback.md
---

# 使用自己的域名接收 OAuth 回调

使用自己的 Google OAuth 应用时，可以把授权回调地址放在自己的域名下。例如，Google 先回调 `https://auth.example.com/oauth/callback`，你的服务再返回 302，将浏览器跳转到 Connector 完成授权。

你需要在自己的域名上部署一个 HTTPS 回调端点。域名和证书由你管理，Connector 继续负责换取 token、保存凭据和刷新 token。

本方案适用于 Connector for SaaS 中使用**自定义 Client**、通过 GET 查询参数返回授权结果的 OAuth provider。先按 [Connector for SaaS 使用指南](/zh-cn/docs/connector-saas/)创建 Project。使用系统 Client 时，继续使用 Console 提供的默认回调地址。

## 准备三个地址

以下地址都以生产环境为例：

| 地址 | 示例 | 填在哪里 |
| --- | --- | --- |
| 你的 OAuth 回调地址 | `https://auth.example.com/oauth/callback` | Google OAuth client 的 Authorized redirect URIs，以及 OOMOL 服务配置的「自定义回调地址」。两处完全一致。 |
| Connector 回调地址 | `https://connector.oomol.com/v1/oauth/callback` | 你的回调端点返回的 302 `Location`。 |
| 产品返回地址 | `https://app.example.com/connections/done` | 创建授权链接时的 `returnUri`，用于显示连接结果。 |

授权顺序是：Google → 你的回调端点 → Connector 回调 → 产品返回地址。浏览器会经过 Connector 域名。

## 第 1 步：部署 302 回调端点

在 `auth.example.com` 部署 `/oauth/callback`，收到请求后立即返回 302。下面的代码可以保存为 `oauth-callback.ts`，使用 `bun run oauth-callback.ts` 启动，再由你现有的网关提供 HTTPS：

```ts
Bun.serve({
  routes: {
    "/oauth/callback": {
      GET(request) {
        const query = new URL(request.url).search;
        return new Response(null, {
          status: 302,
          headers: {
            Location: `https://connector.oomol.com/v1/oauth/callback${query}`,
            "Cache-Control": "no-store",
            "Referrer-Policy": "no-referrer",
          },
        });
      },
    },
  },
});
```

把回调端点接入已有服务时，保留相同的返回逻辑。`Location` 的目标固定为 Connector 回调地址，直接拼接收到的原始查询字符串。

- 保留全部参数及其原始编码，包括 `code`、`state`、错误参数，以及 QuickBooks 的 `realmId`、Shopify 的 `hmac` 等 provider 参数。不要先用 `URLSearchParams` 解码再重建。
- 此端点直接返回 302，不需要登录、换取 token 或消费 `state`。不要从服务端 `fetch` Connector 回调；需要由浏览器跟随跳转。
- 关闭这条路由在应用、网关、CDN、错误上报和 tracing 中的查询参数采集，避免记录授权码和 `state`。端点直接返回响应，不渲染页面或加载第三方资源。

## 第 2 步：在 Google 登记回调地址

在 [Google Cloud Console](https://console.cloud.google.com/) 中，创建或打开你的 **Web application** OAuth client，把以下地址加入 **Authorized redirect URIs**：

```text
https://auth.example.com/oauth/callback
```

这里填写你部署的 HTTPS 地址。协议、主机、路径、尾部斜杠和已有查询参数都要与 OOMOL 中的配置一致。Google 对 redirect URI 的要求见 [Web Server OAuth 文档](https://developers.google.com/identity/protocols/oauth2/web-server#creatingcred)。

提交品牌验证时，在 [Google Search Console](https://search.google.com/search-console) 验证根域名 `example.com` 的所有权，并将其加入 OAuth 应用的 **Authorized domains**。用于域名验证的 Google 账号需要关联到对应的 Google Cloud 项目。首页、隐私政策、其他回调地址或 JavaScript origins 使用的域名也在验证范围内，详见 [Google 品牌验证要求](https://developers.google.com/identity/protocols/oauth2/production-readiness/brand-verification#authorized-domains)。

自定义回调地址解决的是回调域名归属问题。应用仍需满足 Google 的其他品牌验证要求；使用敏感或受限 scopes 时，也需按要求完成相应审核。

## 第 3 步：在 OOMOL 配置自定义 Client

在 [OOMOL Console](https://console.oomol.com/) 中：

1. 打开「项目」，进入目标项目的「服务配置」。
2. 点击「创建服务配置」，选择 `Gmail` 和 `OAuth2`。
3. 将「Client 配置来源」设为「自定义 Client」，填写 Google 的 Client ID 和 Client Secret。
4. 在「自定义回调地址（可选）」中填写 `https://auth.example.com/oauth/callback`。
5. 创建后复制服务配置 ID，作为后端的 `providerConfigId`。

「默认回调地址」是转发代码中的目标地址。自定义回调地址留空时，Google 会直接回调这个默认地址。

也可以由后端使用 Project API key 调用 `POST https://connector.oomol.com/v1/saas/projects/:projectId/provider-configs`，请求头为 `Authorization: Bearer <project-api-key>`，请求体如下：

```json
{
  "service": "gmail",
  "slug": "gmail-custom",
  "displayName": "Gmail",
  "authType": "oauth2",
  "oauth": {
    "clientConfigSource": "custom",
    "clientId": "<google-client-id>",
    "clientSecret": "<google-client-secret>",
    "redirectUri": "https://auth.example.com/oauth/callback",
    "scopes": ["https://www.googleapis.com/auth/gmail.readonly"]
  }
}
```

Project API key 和 Client Secret 只在可信后端或 Console 中使用。示例 scope 用于只读 Gmail；按实际操作配置所需权限。

`oauth.redirectUri` 必须使用 HTTPS，不能包含用户名、密码或 `#` 片段。地址中可以预设 `tenant` 等普通查询参数，但不能预设 `state`、`code`、`error`、`error_description`、`error_uri` 或 `scope`，包括这些参数名的百分号编码形式，以免与授权结果冲突。这些限制针对登记的地址；转发授权结果时仍需完整保留 provider 返回的参数。

只有使用自定义 Client（`clientConfigSource: "custom"`）时才能设置 `redirectUri`。配置查询返回的 `callbackUrl` 是实际交给 provider 的回调地址。已有配置需要更换回调地址时，创建新服务配置并用新 ID 发起授权。

## 第 4 步：发起授权并验证连接

你的后端继续使用原来的授权链接接口，传入新建的服务配置 ID：

```http
POST https://connector.oomol.com/v1/saas/connected-accounts/link
Authorization: Bearer <project-api-key>
Content-Type: application/json
```

```json
{
  "providerConfigId": "<provider-config-id>",
  "userId": "customer-1",
  "returnUri": "https://app.example.com/connections/done"
}
```

后端先保存响应中的 `data.id` 及其所属用户，再让浏览器打开 `data.authorizationUrl`。用户授权后，你的端点把完整查询字符串交给 Connector；Connector 使用登记的自定义回调地址换取 token，再跳转到 `returnUri`。

保存账号绑定前，由后端查询连接请求并确认用户和配置相符。完整步骤见 [SaaS 授权与连接结果校验](/zh-cn/docs/connector-saas/#第-4-步让终端用户连接账号)。使用 SDK 时，继续调用 [ProjectConnector](/zh-cn/docs/project-connector/) 的连接方法，并传入对应的 `providerConfigId`。

上线前，可以先用不含真实授权数据的请求检查转发：

```sh
curl -sS -D - -o /dev/null \
  'https://auth.example.com/oauth/callback?code=test%2Bvalue&state=test-state'
```

响应应为 302，`Location` 应为 `https://connector.oomol.com/v1/oauth/callback?code=test%2Bvalue&state=test-state`。然后完成一次真实授权，并测试取消授权，确认两种结果都能返回产品页面。

## 常见问题

| 现象 | 检查项 |
| --- | --- |
| Google 返回 `redirect_uri_mismatch` | Google 登记地址与 `oauth.redirectUri` 是否完全一致，当前授权是否使用了正确的服务配置 ID。 |
| 回调报 `state` 缺失、无效，或 provider 签名错误 | 网关和应用是否原样保留查询字符串，是否错误地将它解码、重建、丢弃或记录后重复消费。 |
| QuickBooks 回调缺少 `realmId` | 是否只转发了 `code` 和 `state`。转发完整查询字符串。 |
| 端点返回页面或调用成功，浏览器却没有继续跳转 | 应直接向浏览器返回 HTTP 302 和 `Location`，而不是在服务端请求 Connector。 |
| 希望授权完成后回到产品页面 | 创建授权链接时设置 `returnUri`。自定义回调端点始终先转发到 Connector。 |
