How to Set Up OAuth Authentication in OpenSEO Hosted Mode: Complete Guide
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:
- User clicks Connect with Google in the Integrations UI
- OpenSEO redirects to Google's consent screen using the platform's client ID
- After authorization, Google redirects to
/api/gsc/oauth/callback - The server exchanges the code for access and refresh tokens
- Tokens are encrypted with the internal Better Auth secret and stored in the database
- 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 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 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 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
// 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
// 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
// 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 |
| Source files involved | oauth-provider.ts, 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 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.soincludes fully managed Google OAuth with no setup required - The platform supplies its own
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET, andBETTER_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,src/server/mcp/transport.ts,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
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, 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.
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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →