How to Configure OpenSEO to Connect with Google Analytics 4: Hosted and Self-Hosted Setup
To configure OpenSEO for Google Analytics 4, enable the Google Analytics Admin and Data APIs, set your GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment variables for self-hosted deployments, then use the project settings UI to authenticate and select your GA4 property.
OpenSEO supports both hosted (managed) and self-hosted authentication modes for Google Analytics 4 integration. The connection uses OAuth 2.0 with read-only scopes to pull analytics data into your SEO dashboards without requiring write permissions.
Configuration Architecture
OpenSEO implements GA4 connectivity through a provider-based OAuth system defined in src/shared/ga4.ts. The integration supports two distinct operational modes:
Hosted Mode (Default): Uses OpenSEO's pre-configured Better Auth OAuth application. When users click Connect with Google, the system redirects through the managed authentication flow via authClient.oauth2.link.
Self-Hosted Mode: Requires you to provide your own Google Cloud OAuth 2.0 credentials via environment variables. This mode is mandatory for private infrastructure deployments and is handled by startSelfHostedGa4Link in src/serverFunctions/ga4.ts.
The architecture separates concerns between shared constants (src/shared/ga4.ts), client-side initiation (src/client/features/integrations/startGoogleLink.ts), and server-side API operations (src/serverFunctions/ga4.ts).
Prerequisites and Environment Setup
Before connecting GA4, you must enable the required APIs and configure authentication credentials.
Enable Google Analytics APIs
- Visit the Google Cloud Console for your project.
- Enable the Google Analytics Admin API and Google Analytics Data API.
- Ensure your Google account has Viewer or higher permissions on the target GA4 properties.
Configure OAuth Credentials (Self-Hosted Only)
If running OpenSEO on your own infrastructure:
- Create a Web application OAuth 2.0 client ID in Google Cloud Console.
- Add the authorized redirect URI:
https://your-domain.com/api/ga4/oauth/callback. - Copy the Client ID and Client Secret for the next step.
Set Environment Variables
Add the following to your deployment environment (e.g., .env file for Docker or Cloudflare Workers):
GOOGLE_CLIENT_ID=your-web-app-client-id
GOOGLE_CLIENT_SECRET=your-web-app-client-secret
BETTER_AUTH_SECRET=your-random-32-character-secret
Generate the BETTER_AUTH_SECRET using:
openssl rand -base64 32
For hosted deployments, omit the Google credential variables; the platform uses its managed OAuth application automatically.
Connecting GA4 to Your Project
Once environment variables are configured, connect your property through the OpenSEO interface:
- Navigate to Project Settings → Analytics.
- Click Connect with Google.
- Approve the read-only OAuth scopes (
analytics.readonly) on Google's consent screen. - Select your GA4 property from the list populated by
listGa4Properties.
This UI workflow calls startGoogleLink("ga4", callbackURL) from src/client/features/integrations/startGoogleLink.ts, which detects your authentication mode via isHostedClientAuthMode() and routes to either the hosted OAuth handler or the self-hosted flow. After selection, the system invokes setGa4Property to persist the property binding, storing the encrypted OAuth tokens via Better Auth.
Key Implementation Files
src/shared/ga4.ts— Defines GA4 integration constants including the provider IDga4, required scopes array, andGA4_INTEGRATIONconfiguration flags.src/client/features/integrations/startGoogleLink.ts— Exports thestartGoogleLinkfunction that initiates the OAuth flow and handles the redirect logic for both hosted and self-hosted modes.src/serverFunctions/ga4.ts— Contains server functions includinggetGa4Connection,listGa4Properties,setGa4Property,disconnectGa4, andstartSelfHostedGa4Link.docs/SELF_HOSTING_GOOGLE_ANALYTICS.md— Official documentation for self-hosted deployments covering environment variable requirements and Google Cloud Console configuration.
Code Examples
Initiating Self-Hosted GA4 Authentication
When running in self-hosted mode, the server generates the authorization URL:
// src/serverFunctions/ga4.ts
export const startSelfHostedGa4Link = createServerFn({ method: "POST" })
.middleware(requireAuthenticatedContext)
.validator(startSelfHostedLinkSchema)
.handler(async ({ data, context }) => ({
url: await createSelfHostedGoogleAuthorizationUrl({
integration: GA4_INTEGRATION,
user: { userId: context.userId, userEmail: context.userEmail },
callbackURL: data.callbackURL,
publicOrigin: getPublicOrigin(getRequest()),
}),
}));
Client-Side OAuth Entry Point
The UI uses this unified function regardless of deployment mode:
// src/client/features/integrations/startGoogleLink.ts
export async function startGoogleLink(
provider: "gsc" | "ga4",
callbackURL: string,
) {
const config = googleProviders[provider];
if (!isHostedClientAuthMode()) {
const res = await config.startSelfHosted({ data: { callbackURL } });
window.location.href = res.url;
return;
}
const res = await authClient.oauth2.link({
providerId: config.providerId,
callbackURL,
});
if (res.data?.url) window.location.href = res.url;
}
Setting the GA4 Property
After OAuth completion, the selected property is stored:
// src/serverFunctions/ga4.ts
export const setGa4Property = createServerFn({ method: "POST" })
.middleware(requireProjectContext)
.validator(setPropertySchema)
.handler(async ({ data, context }) => {
const connection = await Ga4Service.setProperty({
projectId: context.projectId,
organizationId: context.organizationId,
accountId: data.accountId,
propertyId: data.propertyId,
userId: context.userId,
});
return {
connected: true as const,
propertyId: connection.propertyId,
propertyDisplayName: connection.propertyDisplayName,
};
});
Troubleshooting Common Configuration Errors
redirect_uri_mismatch: The callback URL in Google Cloud Console does not match your deployment origin exactly. Fix: Verify the scheme, domain, and path (/api/ga4/oauth/callback) match yourpublicOriginsetting.- No properties listed: The Google Analytics Admin API is disabled or your account lacks permissions. Fix: Enable the Admin API and ensure your Google account has Viewer access on the target properties.
- Connection expires frequently: The OAuth client is in "Testing" status, issuing short-lived refresh tokens. Fix: Promote the OAuth app to "Production" status in Google Cloud Console.
- Hosted mode unavailable: The
GA4_INTEGRATION.pendingflag is set totrueinsrc/shared/ga4.ts. Fix: Wait for the hosted OAuth app approval or switch to self-hosted mode by configuringGOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRET.
Summary
- OpenSEO offers hosted and self-hosted GA4 integration modes, configurable via environment variables.
- Self-hosted deployments require
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET, andBETTER_AUTH_SECRETfor secure token encryption. - The integration uses read-only OAuth scopes defined in
src/shared/ga4.tsto securely access analytics data without write permissions. - Core functions
startGoogleLink,startSelfHostedGa4Link, andsetGa4Propertyhandle the authentication and property binding workflow. - Refer to
docs/SELF_HOSTING_GOOGLE_ANALYTICS.mdfor detailed Google Cloud Console setup instructions.
Frequently Asked Questions
What environment variables are required for self-hosted GA4 integration?
You must set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET from your Google Cloud OAuth web application credentials, plus BETTER_AUTH_SECRET for token encryption. Generate the auth secret with openssl rand -base64 32. Hosted deployments do not require the Google credentials as they use the managed Better Auth application.
Why does my GA4 connection show no available properties?
This occurs when the Google Analytics Admin API is disabled in Google Cloud Console, or your Google account lacks permissions on any GA4 properties. Enable the API and verify your account has at least Viewer access on the target properties. The listGa4Properties function filters results based on your authenticated Google identity.
How do I switch from hosted to self-hosted GA4 authentication?
Set the GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment variables in your deployment. The startGoogleLink function in src/client/features/integrations/startGoogleLink.ts automatically detects these variables via isHostedClientAuthMode() and routes to startSelfHostedGa4Link instead of the hosted OAuth flow. Restart your application after setting the variables.
Is the GA4 integration read-only?
Yes. The integration requests only the analytics.readonly scope as defined in the scopes array within src/shared/ga4.ts. OpenSEO cannot modify your GA4 configuration or data; it only pulls metrics for reporting purposes, storing encrypted tokens via Better Auth's account table.
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 →