# How to Set Up OAuth Authentication in OpenSEO Hosted Mode: Complete Guide

> Easily set up OAuth authentication in OpenSEO hosted mode. Connect with Google in the UI and OpenSEO handles all credentials, token exchange, and encryption automatically.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-09-05

---

**In OpenSEO hosted mode, Google OAuth authentication is pre-configured and requires zero manual setup—users simply click "Connect with Google" in the Integrations UI and the platform handles all credentials, token exchange, and encryption automatically.**

OpenSEO supports two deployment models: **hosted** (the public SaaS at `app.openseo.so`) and **self-hosted** (Docker, Cloudflare Workers, private infrastructure). This guide explains how OAuth authentication works in **hosted mode**, where the entire OAuth infrastructure is managed by the OpenSEO platform and requires no configuration from users.

---

## Understanding OpenSEO Hosted Mode OAuth Architecture

When you use OpenSEO in hosted mode, the platform operates its own Google OAuth application. This eliminates the need for you to create a Google Cloud project, generate client credentials, or manage environment variables like `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, or `BETTER_AUTH_SECRET`.

The OAuth flow follows this sequence:

1. User clicks **Connect with Google** in the Integrations UI
2. OpenSEO redirects to Google's consent screen using the platform's client ID
3. After authorization, Google redirects to `/api/gsc/oauth/callback`
4. The server exchanges the code for access and refresh tokens
5. Tokens are encrypted with the internal **Better Auth secret** and stored in the database
6. Subsequent API calls decrypt tokens on-demand and refresh them automatically

---

## Key Components of the Hosted OAuth System

The OAuth implementation spans several source files in the OpenSEO repository:

### OAuth Provider: Token Exchange and Encryption

**[`src/server/mcp/oauth-provider.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/oauth-provider.ts)** implements the `/api/gsc/oauth/callback` handler. This module receives the authorization code from Google, validates the request origin, exchanges the code for tokens, and encrypts the grant payload using the hosted platform's Better Auth secret.

### MCP Transport: Origin Validation

**[`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts)** ensures that OAuth-related requests originate only from the official hosted domain (`app.openseo.so`). This security layer prevents credential misuse from unauthorized domains attempting to leverage the built-in OAuth client.

### Search Console Tools: Configuration Detection

**[`src/server/mcp/tools/search-console-tools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/search-console-tools.ts)** checks whether OAuth is properly configured. In hosted mode, this always passes; in self-hosted mode, it returns the error "Google OAuth client not configured" when required environment variables are missing.

### Better Auth Secret: Encryption Layer

The `BETTER_AUTH_SECRET` used to encrypt stored OAuth tokens is managed entirely by the OpenSEO hosted environment and is never exposed to customers or visible in any configuration interface.

---

## How to Connect Your Google Account in Hosted Mode

Since OpenSEO hosted mode requires **zero configuration**, the entire process happens through the web interface:

### Step 1: Navigate to Integrations

Log in to `https://app.openseo.so` and open your project settings. Select the **Integrations** tab to view available connections.

### Step 2: Initiate Google Connection

Click the **Connect with Google** button. The frontend calls the internal endpoint `/api/gsc/oauth/start`, which builds the Google authorization URL using the platform's hosted client credentials.

### Step 3: Authorize on Google's Consent Screen

You'll be redirected to Google's OAuth consent screen showing "OpenSEO" as the requesting application. Grant the requested permissions for Google Search Console access.

### Step 4: Automatic Redirect and Token Storage

After authorization, Google redirects back to `https://app.openseo.so/api/gsc/oauth/callback`. The server completes the token exchange and stores your encrypted credentials. You're immediately returned to the OpenSEO dashboard with Search Console data available.

---

## Code Implementation: How Hosted OAuth Works

### Client-Side: Initiating the OAuth Flow

```tsx
// React component triggering the hosted OAuth flow
import { useState } from "react";

export function GoogleConnectButton() {
  const [loading, setLoading] = useState(false);

  async function startOAuth() {
    setLoading(true);
    // OpenSEO hosted endpoint builds the Google authorization URL automatically
    const resp = await fetch("/api/gsc/oauth/start", { method: "POST" });
    const { redirectUrl } = await resp.json();
    window.location.href = redirectUrl;
  }

  return (
    <button onClick={startOAuth} disabled={loading}>
      {loading ? "Redirecting…" : "Connect with Google"}
    </button>
  );
}

```

The `/api/gsc/oauth/start` endpoint constructs the Google authorization URL using OpenSEO's hosted `GOOGLE_CLIENT_ID` and the current origin `https://app.openseo.so`.

### Server-Side: Processing the OAuth Callback

```ts
// src/server/mcp/oauth-provider.ts – simplified callback handler
import { json } from "@remix-run/node";
import { getHostedBaseUrl } from "@/server/mcp/urls";

export async function gscOAuthCallback(request: Request) {
  const url = new URL(request.url);
  const code = url.searchParams.get("code");
  if (!code) return json({ error: "Missing code" }, { status: 400 });

  // Exchange authorization code for tokens using hosted credentials
  const tokenResponse = await exchangeCodeForGoogleTokens(code, {
    clientId: process.env.GOOGLE_CLIENT_ID!,          // provided by OpenSEO platform
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!, // provided by OpenSEO platform
    redirectUri: `${getHostedBaseUrl()}/api/gsc/oauth/callback`,
  });

  // Encrypt and persist the grant using the internal Better Auth secret
  await storeEncryptedGrant(tokenResponse);

  return json({ success: true }, { status: 200 });
}

```

All sensitive credentials (`GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `BETTER_AUTH_SECRET`) are injected by the hosted Cloudflare Workers environment—no user action required.

### Token Usage: Decrypting and Refreshing Automatically

```ts
// src/server/lib/gscClient.ts – calling Google Search Console API
import { decryptGrant } from "@/server/lib/better-auth";

export async function fetchSearchConsoleData(projectId: string) {
  const encryptedGrant = await getGrantForProject(projectId);
  const { accessToken, refreshToken } = await decryptGrant(encryptedGrant);

  const response = await fetch(
    "https://searchconsole.googleapis.com/webmasters/v3/sites",
    {
      headers: { Authorization: `Bearer ${accessToken}` },
    },
  );

  // Token refresh happens automatically when access tokens expire
  return response.json();
}

```

The decryption operation uses the hosted Better Auth secret transparently. Token refresh is handled internally without user intervention.

---

## Hosted Mode vs. Self-Hosted Mode: OAuth Comparison

| Aspect | Hosted Mode | Self-Hosted Mode |
|--------|-------------|------------------|
| **Google client credentials** | Managed by OpenSEO platform | Must create your own Google Cloud OAuth app |
| **BETTER_AUTH_SECRET** | Managed by OpenSEO platform | Must generate and configure your own secret |
| **OAuth callback URL** | Fixed: `app.openseo.so/api/gsc/oauth/callback` | Configurable to your domain |
| **Setup steps** | Click "Connect with Google" only | Follow full manual setup in [`docs/SELF_HOSTING_GOOGLE_SEARCH_CONSOLE.md`](https://github.com/every-app/open-seo/blob/main/docs/SELF_HOSTING_GOOGLE_SEARCH_CONSOLE.md) |
| **Source files involved** | [`oauth-provider.ts`](https://github.com/every-app/open-seo/blob/main/oauth-provider.ts), [`transport.ts`](https://github.com/every-app/open-seo/blob/main/transport.ts) | Same files, but environment-dependent behavior |

---

## Troubleshooting Common Issues

### "Google OAuth client not configured" Error

This error originates from **[`src/server/mcp/tools/search-console-tools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/search-console-tools.ts)** and only appears in **self-hosted** deployments. In hosted mode, if you see this error, contact OpenSEO support—this indicates a platform-side issue, not a configuration problem on your end.

### Redirect URI Mismatch Errors

OpenSEO hosted mode uses a fixed redirect URI. If you encounter redirect URI mismatch errors, ensure you're accessing the application directly at `https://app.openseo.so` and not through a proxy or custom domain that modifies the origin.

### Token Expiration and Re-Connection

Access tokens expire periodically, but OpenSEO automatically refreshes them using stored refresh tokens. If you need to **reconnect** your Google account (for example, to change permissions), disconnect through the Integrations UI and repeat the connection flow—no credential re-entry required.

---

## Summary

- OpenSEO hosted mode at `app.openseo.so` includes **fully managed Google OAuth** with no setup required
- The platform supplies its own `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `BETTER_AUTH_SECRET`
- Users connect accounts by clicking **Connect with Google**—the OAuth flow, token encryption, and storage are automatic
- Core implementation files: **[`src/server/mcp/oauth-provider.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/oauth-provider.ts)**, **[`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts)**, **[`src/server/mcp/tools/search-console-tools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/search-console-tools.ts)**
- Encryption uses the internal Better Auth secret; tokens are never exposed to clients
- Self-hosted deployments require manual OAuth configuration per **[`docs/SELF_HOSTING_GOOGLE_SEARCH_CONSOLE.md`](https://github.com/every-app/open-seo/blob/main/docs/SELF_HOSTING_GOOGLE_SEARCH_CONSOLE.md)**

---

## Frequently Asked Questions

### Do I need to create a Google Cloud project to use OpenSEO hosted mode?

No. OpenSEO's hosted deployment operates its own Google OAuth application. You never need to create API credentials, configure consent screens, or manage OAuth client settings. The entire process is abstracted—you simply authorize the pre-configured "OpenSEO" application when prompted.

### Where are my Google access tokens stored in OpenSEO hosted mode?

Encrypted in OpenSEO's database using AES encryption with the platform's internal `BETTER_AUTH_SECRET`. According to the source code in [`src/server/mcp/oauth-provider.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/oauth-provider.ts), tokens are encrypted before storage and only decrypted on-demand during API calls. The raw credentials never reach your browser or any client-side code.

### Can I use my own Google OAuth credentials with OpenSEO hosted mode?

No. The hosted platform at `app.openseo.so` uses a fixed OAuth client configuration managed by the OpenSEO team. If you require custom OAuth credentials (for example, for enterprise security policies or whitelisted domains), you must deploy OpenSEO in **self-hosted mode**, where you supply your own `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `BETTER_AUTH_SECRET` as documented in [`docs/SELF_HOSTING_GOOGLE_SEARCH_CONSOLE.md`](https://github.com/every-app/open-seo/blob/main/docs/SELF_HOSTING_GOOGLE_SEARCH_CONSOLE.md).

### What happens if OpenSEO's Better Auth secret is rotated?

Secret rotation is handled transparently by the OpenSEO platform. The hosted Cloudflare Workers environment updates the `BETTER_AUTH_SECRET` without user-visible changes. Existing encrypted grants remain readable because the rotation process maintains backward compatibility or re-encrypts stored data during deployment.