---
author: OOMOL
author_url: https://oomol.com/about/
title: Use Your Own Domain for OAuth Callbacks
description: Configure a custom OAuth callback URL for Connector for SaaS,
  receive the authorization response on your domain, and redirect it to
  Connector with HTTP 302.
lang: en
canonical_url: https://oomol.com/docs/saas-oauth-callback/
markdown_url: https://oomol.com/docs/saas-oauth-callback.md
---

# Use Your Own Domain for OAuth Callbacks

When using your own Google OAuth app, you can receive the authorization callback on your domain. For example, Google redirects to `https://auth.example.com/oauth/callback`, and your endpoint returns a 302 redirect to Connector to complete authorization.

Deploy an HTTPS callback endpoint on your domain. You manage the domain and certificate; Connector handles the token exchange, credential storage, and token refresh.

This setup applies to Connector for SaaS providers using a **custom client** that return OAuth results through GET query parameters. Create a Project with the [Connector for SaaS guide](/docs/connector-saas/) first. When using a system client, use the default callback URL provided by Console.

## Prepare the three URLs

These examples use the production Connector service:

| URL | Example | Where to configure it |
| --- | --- | --- |
| Your OAuth callback | `https://auth.example.com/oauth/callback` | The Google client's Authorized redirect URIs and the OOMOL provider config's Custom callback URL. Use exactly the same URL in both. |
| Connector callback | `https://connector.oomol.com/v1/oauth/callback` | The 302 `Location` returned by your callback endpoint. |
| Product return URL | `https://app.example.com/connections/done` | The `returnUri` when creating an authorization link, where your product displays the connection result. |

The flow is Google → your callback endpoint → Connector callback → product return URL. The browser passes through the Connector domain.

## Step 1: Deploy the 302 endpoint

Serve `/oauth/callback` on `auth.example.com` and return a 302 response immediately. Save this code as `oauth-callback.ts`, start it with `bun run oauth-callback.ts`, and expose it through your existing HTTPS gateway:

```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",
          },
        });
      },
    },
  },
});
```

Use the same response logic when adding the endpoint to an existing service. Keep the Connector callback as the fixed `Location` destination and append the original query string.

- Preserve every parameter and its original encoding, including `code`, `state`, errors, and provider-specific fields such as QuickBooks `realmId` or Shopify `hmac`. Do not decode and rebuild the query with `URLSearchParams`.
- Return the 302 directly. This endpoint does not require sign-in, exchange tokens, or consume `state`. Do not `fetch` the Connector callback from your server; the browser must follow the redirect.
- Disable query-string capture for this route in application logs, gateways, CDNs, error reporting, and tracing so that authorization codes and `state` are not recorded. Return the response without rendering a page or loading third-party resources.

## Step 2: Register the callback with Google

In [Google Cloud Console](https://console.cloud.google.com/), create or open your **Web application** OAuth client. Add this URL to **Authorized redirect URIs**:

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

Use the HTTPS URL you deployed. Its scheme, host, path, trailing slash, and any existing query parameters must match the OOMOL config. See Google's [web server OAuth documentation](https://developers.google.com/identity/protocols/oauth2/web-server#creatingcred) for redirect URI requirements.

For brand verification, verify ownership of the root domain, `example.com`, in [Google Search Console](https://search.google.com/search-console) and add it to your OAuth app's **Authorized domains**. The Google account used for domain verification must be associated with the Google Cloud project. Domains used by your home page, privacy policy, other redirect URIs, or JavaScript origins are also in scope. See [Google's brand verification requirements](https://developers.google.com/identity/protocols/oauth2/production-readiness/brand-verification#authorized-domains).

Registering a callback on your own domain lets you verify that domain with Google. Your app must still meet Google's other brand requirements and complete any required sensitive or restricted scope reviews.

## Step 3: Configure a custom client in OOMOL

In [OOMOL Console](https://console.oomol.com/):

1. Open **Projects**, select the project, and open **Provider configs**.
2. Click **Create provider config** and select `Gmail` with `OAuth2`.
3. Set **Client config source** to **Custom client** and enter your Google Client ID and Client Secret.
4. Enter `https://auth.example.com/oauth/callback` in **Custom callback URL (optional)**.
5. Create the config and copy its Provider Config ID for your backend's `providerConfigId`.

The **Default callback URL** is the destination in your forwarding code. Leave the custom callback empty to have Google redirect directly to the default URL.

Your backend can also call `POST https://connector.oomol.com/v1/saas/projects/:projectId/provider-configs` with a Project API key, an `Authorization: Bearer <project-api-key>` header, and this body:

```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"]
  }
}
```

Use the Project API key and Client Secret only in trusted backend code or Console. The example scope allows read-only Gmail access; choose the permissions needed by your operations.

`oauth.redirectUri` must use HTTPS and cannot contain a username, password, or `#` fragment. It may include ordinary query parameters such as `tenant`, but must not predefine `state`, `code`, `error`, `error_description`, `error_uri`, or `scope`, including percent-encoded forms of those names. These restrictions apply to the registered URL; your endpoint must still forward all parameters received from the provider.

`redirectUri` is accepted only with `clientConfigSource: "custom"`. The `callbackUrl` returned by config queries is the URL actually sent to the provider. To change an existing config's callback URL, create a new provider config and start authorization with its ID.

## Step 4: Start authorization and verify the connection

Your backend uses the existing authorization-link endpoint with the new Provider Config 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"
}
```

Save the response's `data.id` and its user association on your backend before opening `data.authorizationUrl` in the browser. After authorization, your endpoint forwards the full query string to Connector. Connector exchanges the code using the registered custom callback URL, then redirects to `returnUri`.

Before storing an account binding, query the connection request from your backend and verify its user and config. Follow the [SaaS authorization and connection verification steps](/docs/connector-saas/#step-4-let-the-end-user-connect-an-account). With the SDK, continue using the [ProjectConnector](/docs/project-connector/) connection methods and pass the corresponding `providerConfigId`.

Before going live, check the forwarder with dummy callback data:

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

Expect a 302 response with `Location: https://connector.oomol.com/v1/oauth/callback?code=test%2Bvalue&state=test-state`. Then complete a real authorization and test cancellation. Confirm that both outcomes return to your product.

## Troubleshooting

| Symptom | What to check |
| --- | --- |
| Google returns `redirect_uri_mismatch` | The URL registered with Google must exactly match `oauth.redirectUri`. Check that the authorization request uses the correct Provider Config ID. |
| Missing or invalid `state`, or a provider signature error | Your gateway and app must preserve the original query string. Check for decoding, reconstruction, dropped parameters, or consuming the callback more than once. |
| QuickBooks reports a missing `realmId` | Forward the complete query string rather than selecting only `code` and `state`. |
| Your endpoint responds, but the browser does not continue | Return HTTP 302 and `Location` to the browser instead of requesting Connector from your server. |
| The user needs to return to your product after authorization | Set `returnUri` when creating the authorization link. Your custom callback endpoint must forward to Connector first. |
