How GA4 OAuth Setup Works with the `npm run ga4:oauth` Script in awesome-gpt-image-2

The npm run ga4:oauth command runs a Node.js script that automates Google's OAuth 2.0 Authorization Code flow to generate a long-lived refresh token for Google Analytics 4 data access.

The awesome-gpt-image-2 repository implements a complete GA4 OAuth automation that handles everything from consent URL generation to token exchange in a single command. This setup allows the application to query GA4 traffic data server-side without exposing credentials to the client.

What the GA4 OAuth Script Does

The OAuth flow implemented in scripts/google-analytics-oauth.mjs follows the standard Google OAuth 2.0 pattern with six distinct phases:

Step Action Implementation Location
1 Generates OAuth consent URL with analytics.readonly scope scripts/google-analytics-oauth.mjs
2 Prompts user to authenticate and authorize in browser Terminal output from same script
3 Captures authorization code via localhost callback server Port 8080 handler in same script
4 Exchanges code for access_token and refresh_token POST to oauth2.googleapis.com/token
5 Outputs refresh token for environment configuration Terminal output
6 Runtime code uses token to query GA4 Reporting API src/_lib/ga4.js via api/admin/metrics.js

Running the GA4 OAuth Script

Execute the npm script to start the authentication flow:

npm run ga4:oauth

This prints a URL similar to:


https://accounts.google.com/o/oauth2/auth?
  client_id=xxx.apps.googleusercontent.com
  &redirect_uri=http://localhost:8080/oauth2callback
  &scope=https://www.googleapis.com/auth/analytics.readonly
  &response_type=code
  &access_type=offline

The script requires these environment variables in your .env file:

GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxx

Without these values, the script cannot construct the OAuth request or authenticate with Google's token endpoint.

How the Callback Server Captures the Authorization Code

Once you open the URL and grant permission, Google redirects to http://localhost:8080/oauth2callback?code=AUTH_CODE. The script's embedded HTTP server handles this request:

// From scripts/google-analytics-oauth.mjs
const server = createServer(async (req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);
  const code = url.searchParams.get('code');
  
  // Exchange code for tokens
  const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      code,
      client_id: process.env.GOOGLE_CLIENT_ID,
      client_secret: process.env.GOOGLE_CLIENT_SECRET,
      redirect_uri: 'http://localhost:8080/oauth2callback',
      grant_type: 'authorization_code',
    }),
  });
  
  const tokens = await tokenResponse.json();
  console.log('GOOGLE_ANALYTICS_REFRESH_TOKEN=' + tokens.refresh_token);
  
  res.end('Authorization successful. Return to terminal.');
  server.close();
});

server.listen(8080);

The server automatically shuts down after capturing the code, making this a one-time setup process.

Configuring the Refresh Token in Your Environment

Copy the printed refresh_token and add it to your deployment environment:

For Vercel (production):

  • Add GOOGLE_ANALYTICS_REFRESH_TOKEN as a Sensitive environment variable
  • Add VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX
  • Add GA4_PROPERTY_ID=123456789

For local development:


# .env.local

GOOGLE_ANALYTICS_REFRESH_TOKEN=1//0gX...
VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX
GA4_PROPERTY_ID=123456789

How the Application Uses the GA4 Token at Runtime

The refresh token enables server-side GA4 data access through a chain of modules:

Frontend script injection (src/main.jsx lines 584-589):

const existingScript = document.querySelector(`script[data-ga4="${gaMeasurementId}"]`);
if (!existingScript) {
  const script = document.createElement('script');
  script.async = true;
  script.src = `https://www.googletagmanager.com/gtag/js?id=${gaMeasurementId}`;
  script.dataset.ga4 = gaMeasurementId;
  document.head.appendChild(script);
}

Admin metrics API (api/admin/metrics.js):

import { getGa4Traffic, isGa4Configured } from '../_lib/ga4.js';

// getGa4Traffic uses the refresh token to obtain a fresh access token
// and queries the GA4 Reporting API for traffic metrics

The src/_lib/ga4.js module handles the token refresh logic transparently—exchanging the stored refresh token for short-lived access tokens as needed, then querying the GA4 Data API for real-time and historical metrics.

Why This OAuth Approach Matters

This setup provides three critical security benefits:

  1. No client-side secrets — The refresh token never reaches the browser; all GA4 API calls happen server-side
  2. Long-lived authorization — One OAuth flow generates a refresh token valid until explicitly revoked
  3. Minimal permission scope — Only analytics.readonly is requested, following principle of least privilege

The npm run ga4:oauth command encapsulates complexity that would otherwise require manual HTTP requests or external tooling.

Summary

  • The npm run ga4:oauth script in awesome-gpt-image-2 automates the complete GA4 OAuth 2.0 flow
  • It generates consent URLs, runs a temporary localhost server on port 8080, and exchanges codes for refresh tokens
  • The resulting GOOGLE_ANALYTICS_REFRESH_TOKEN enables server-side GA4 data queries without exposing credentials
  • Runtime code in src/_lib/ga4.js manages token refresh and API calls, accessed via api/admin/metrics.js
  • The flow requires GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment variables to function

Frequently Asked Questions

What happens if the localhost:8080 port is already in use?

The script will fail to bind and throw an EADDRINUSE error. Either free the port or modify the redirect_uri constant in scripts/google-analytics-oauth.mjs to use an alternative port, updating both the script and your Google Cloud Console redirect URI configuration.

Can I reuse the same refresh token across multiple deployments?

Yes. The refresh token is tied to your Google account and OAuth client, not to a specific deployment. You can use the same GOOGLE_ANALYTICS_REFRESH_TOKEN value in Vercel production, preview environments, and local development—provided all use the same GOOGLE_CLIENT_ID.

Why does the script request offline access type?

The access_type=offline parameter (included in the consent URL) is required for Google to return a refresh_token. Without this, the authorization would only yield a short-lived access token, forcing you to repeat the OAuth flow repeatedly instead of storing persistent access.

Where is the GA4 data actually fetched in the codebase?

The low-level GA4 API integration lives in src/_lib/ga4.js (imported by api/admin/metrics.js). This module uses the refresh token to call Google's token endpoint for fresh access tokens, then queries the GA4 Data API for metrics like active users and page views.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →