How to Integrate Google Search Console (GSC) with OpenSEO: Complete Setup Guide
OpenSEO supports a first-party Google Search Console integration that pulls click, impression, and position data directly from your Google account using OAuth 2.0 and the webmasters.readonly scope, working in both hosted and self-hosted deployments.
OpenSEO is an open-source SEO platform that includes native support for Google Search Console. The integration allows you to analyze search performance without manually exporting CSV files, using a secure OAuth flow that encrypts tokens at rest. Whether you run the every-app/open-seo repository in the cloud or on your own infrastructure, the architecture supports seamless GSC data retrieval through a type-safe API layer.
Prerequisites and Environment Configuration
Before initiating the OAuth flow, self-hosted instances must configure three critical environment variables to authenticate with Google’s API.
Required Environment Variables
The integration requires the following variables in your deployment environment:
GOOGLE_CLIENT_ID– Your OAuth 2.0 Client ID from Google Cloud ConsoleGOOGLE_CLIENT_SECRET– The corresponding Client SecretBETTER_AUTH_SECRET– A sufficiently long encryption key used by Better-Auth to encrypt OAuth tokens at rest
In src/server/features/gsc/oauth-config.ts, the helper function hasSelfHostedGscConfig validates these variables before surfacing the "Connect with Google" UI. If any variable is missing, the integration remains disabled to prevent runtime errors.
Google Cloud Project Setup
You must create a Google Cloud project and enable the Google Search Console API. Configure the OAuth consent screen as an External app and add authorized test users if your app remains in testing mode. Create OAuth 2.0 credentials (Web application type) and set the authorized redirect URI to https://<your-domain>/api/gsc/oauth/callback.
OAuth Provider Architecture
OpenSEO uses Better-Auth to manage the OAuth handshake, with a dedicated provider configuration for Google Search Console.
Better-Auth Integration
The OAuth provider is defined in src/shared/gsc.ts and consumed by the generic auth plugin in src/lib/auth-config.ts. The provider requests the webmasters.readonly scope, which grants read-only access to GSC data without permission to modify site settings.
// Conceptual flow from src/shared/gsc.ts and src/lib/auth-config.ts
const gscProvider = {
id: "google-search-console",
scopes: ["webmasters.readonly"],
// ... additional OAuth configuration
};
Permission Scopes and Security
The webmasters.readonly scope limits OpenSEO to viewing search analytics, site listings, and URL inspection data. It cannot submit URL removals or change site configurations. All OAuth tokens are encrypted using BETTER_AUTH_SECRET before storage in the database, ensuring credentials remain secure even if database backups are compromised.
Connecting Your GSC Account
The connection process involves two distinct phases: initial authorization and property selection.
The Connection Flow
When users click Integrations → GSC Insights → Connect with Google, the client invokes startSelfHostedGscLink (exposed in src/serverFunctions/gsc.ts). This function:
- Validates the self-hosted configuration via
hasSelfHostedGscConfig - Constructs the authorization URL with the correct
stateparameter - Redirects the user to Google’s OAuth consent screen
After authorization, Google redirects to /api/gsc/oauth/callback, where the system exchanges the code for tokens and stores them via Better-Auth’s encrypted credential store.
Selecting a Property
Once authenticated, the UI calls listGscSites (from src/serverFunctions/gsc.ts) to retrieve all verified properties. The endpoint returns an array of accounts with their associated sites, marking properties that require reconnection due to expired grants.
// Example response from listGscSites
{
accounts: [{
accountId: "acct_456",
email: "admin@example.com",
requiresReconnect: false,
sites: [{
siteUrl: "https://example.com",
isSelected: false,
selectable: true
}]
}]
}
When a user selects a site, the client sends a POST request to setGscSite, which invokes GscService.setSite in src/server/features/gsc/services/GscService.ts. This persists the mapping in the gsc_connection table and associates the OAuth account with the specific OpenSEO project.
Retrieving Search Console Data
After connection, OpenSEO provides several server functions to query GSC data programmatically.
Service Layer Architecture
GscService (src/server/features/gsc/services/GscService.ts) orchestrates all GSC operations. It wraps the low-level HTTP client defined in src/server/lib/gscClient.ts, which handles requests to /sites and /searchAnalytics/query endpoints. The service normalizes API errors into GscApiError and GscTokenError classes, allowing the UI to distinguish between permission issues and transient API failures.
Available API Endpoints
The public API exposed through src/serverFunctions/gsc.ts includes:
getGscGrantStatus– Checks if the current user has an active OAuth grantgetGscConnection– Returns the selected property URL and connection state for a projectlistGscSites– Lists all accessible GSC properties across all connected Google accountssetGscSite– Binds a chosen property to a specific projectdisconnectGsc– Removes the project binding and revokes the OAuth account if unused by other projects
Database Schema and Security
OpenSEO maintains a dedicated schema for GSC connections with encrypted credential storage.
The gsc_connection Table
Defined in src/db/gsc.schema.ts, the gsc_connection table stores:
- project_id – Foreign key to the OpenSEO project
- site_url – The selected GSC property URL
- account_id – Reference to the encrypted OAuth credential stored by Better-Auth
- created_at/updated_at – Timestamps for audit trails
Token Encryption
Better-Auth encrypts OAuth access tokens and refresh tokens using the BETTER_AUTH_SECRET environment variable before writing them to the database. This means raw Google credentials never exist in plain text in the database or application logs.
Step-by-Step Self-Hosted Setup
Follow these steps to integrate Google Search Console in a self-hosted OpenSEO instance:
- Create a Google Cloud project and enable the Google Search Console API
- Configure OAuth consent as an External app and add test users
- Create an OAuth client ID (Web application type) and set the redirect URI to
https://<your-domain>/api/gsc/oauth/callback - Add environment variables (
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET,BETTER_AUTH_SECRET) to your.envfile or deployment dashboard - Restart OpenSEO to load the new configuration
- Navigate to Integrations → GSC Insights and click "Connect with Google"
- Select a verified property from the site picker populated by
listGscSites - Verify connection by checking that the GSC Insights dashboard populates with click and impression data
Programmatic API Usage
You can interact with the GSC integration programmatically using the server functions. These examples assume an authenticated session with appropriate cookies.
Check Grant Status
const status = await fetch('/api/gsc/grant-status', { method: 'GET' })
.then(r => r.json());
// Returns: { connected: true } or { connected: false }
Retrieve Connection Details
const connection = await fetch('/api/gsc/connection', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ projectId: 'proj_123' })
}).then(r => r.json());
// Returns: { connected: true, siteUrl: 'https://example.com', ... }
List Available Sites
const sites = await fetch('/api/gsc/sites', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ projectId: 'proj_123' })
}).then(r => r.json());
// Returns: { accounts: [{ accountId, email, requiresReconnect, sites: [...] }] }
Bind a Site to Project
const result = await fetch('/api/gsc/set-site', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
projectId: 'proj_123',
accountId: 'acct_456',
siteUrl: 'https://example.com'
})
}).then(r => r.json());
// Returns: { connected: true, siteUrl: 'https://example.com' }
Disconnect GSC
const result = await fetch('/api/gsc/disconnect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ projectId: 'proj_123' })
}).then(r => r.json());
// Returns: { connected: false }
Summary
- OpenSEO integrates Google Search Console through a first-party OAuth 2.0 flow using the
webmasters.readonlyscope - Configuration requires three environment variables:
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET, andBETTER_AUTH_SECRET - Architecture includes a Better-Auth provider in
src/shared/gsc.ts, a service layer insrc/server/features/gsc/services/GscService.ts, and API endpoints insrc/serverFunctions/gsc.ts - Security relies on Better-Auth’s encryption for OAuth tokens and the
gsc_connectiontable insrc/db/gsc.schema.tsfor project mappings - Self-hosted deployments must configure Google Cloud OAuth credentials with the redirect URI pointing to
/api/gsc/oauth/callback
Frequently Asked Questions
Do I need to self-host OpenSEO to use Google Search Console integration?
No. OpenSEO supports GSC integration in both hosted and self-hosted deployments. However, self-hosted instances require additional configuration of Google Cloud OAuth credentials and environment variables, while the hosted version handles OAuth provider setup automatically.
What permissions does OpenSEO request from my Google account?
OpenSEO requests the webmasters.readonly OAuth scope, which grants read-only access to your Search Console data. This permission allows OpenSEO to retrieve click counts, impressions, and position data, but cannot modify site settings, remove URLs, or submit sitemaps.
Where are my Google OAuth tokens stored?
OAuth tokens are encrypted at rest using your BETTER_AUTH_SECRET environment variable and stored in the database managed by Better-Auth. The gsc_connection table in src/db/gsc.schema.ts only stores references to these encrypted credentials, not the tokens themselves.
Can I connect multiple Google Search Console properties to one OpenSEO project?
No, each OpenSEO project binds to a single GSC property URL at a time. However, you can disconnect and reconnect to different properties using the setGscSite endpoint, or create separate OpenSEO projects for different GSC properties. The listGscSites endpoint shows all properties available across all Google accounts connected to your user.
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 →