# GA4 OAuth Flow for Analytics Readonly Permission: Complete Implementation Guide

> Master the GA4 OAuth flow for analytics.readonly permission with this Node.js guide. Implement server-side Google Analytics 4 data access easily. Get your refresh token now.

- Repository: [苍何/awesome-gpt-image-2](https://github.com/freestylefly/awesome-gpt-image-2)
- Tags: tutorial
- Published: 2026-08-31

---

**The Awesome‑GPT‑Image‑2 repository implements a standalone Node.js OAuth helper in `scripts/google-analytics-oauth.mjs` that obtains a refresh token with `analytics.readonly` scope for server-side Google Analytics 4 data access.**

This guide explains how the **GA4 OAuth flow** works in the `freestylefly/awesome-gpt-image-2` repository, walking you through the exact authentication process used to securely read analytics data without exposing credentials to the client.

## How the GA4 OAuth Flow Works

The repository's OAuth implementation follows Google's standard **offline access** pattern, forcing a consent screen to guarantee a refresh token is returned. The entire flow is encapsulated in a single CLI script.

### Step 1: Load Client Credentials

The helper first loads your Google Cloud OAuth credentials from environment variables. In `scripts/google-analytics-oauth.mjs` lines 50-52, the script reads:

- `GOOGLE_ANALYTICS_CLIENT_ID`
- `GOOGLE_ANALYTICS_CLIENT_SECRET`

```javascript
// From scripts/google-analytics-oauth.mjs
const clientId = process.env.GOOGLE_ANALYTICS_CLIENT_ID;
const clientSecret = process.env.GOOGLE_ANALYTICS_CLIENT_SECRET;
const redirectUri = process.env.GOOGLE_ANALYTICS_REDIRECT_URI || 'http://localhost:8080/oauth2callback';

```

### Step 2: Build the Authorization URL

Using `google-auth-library`'s `OAuth2Client`, the script constructs the authorization URL at lines 59-64. Critical parameters include:

- `access_type: 'offline'` — Required to receive a refresh token
- `prompt: 'consent'` — Forces the consent screen even if previously authorized
- `scope: 'https://www.googleapis.com/auth/analytics.readonly'` — Read-only GA4 access

```javascript
const authUrl = oauth2Client.generateAuthUrl({
  access_type: 'offline',
  prompt: 'consent',
  scope: ['https://www.googleapis.com/auth/analytics.readonly'],
});

```

### Step 3: Exchange Authorization Code for Tokens

After the user grants consent and Google redirects to your localhost callback, you provide the `code` parameter to the script (lines 70-77). The script exchanges this for tokens at lines 79-84 using `oauth2Client.getToken(code)`.

The response contains both an access token and the critical **refresh token** needed for long-term server-side access.

## Required Environment Variables

Configure these variables before running the OAuth helper, as documented in `.env.example` lines 30-34 and the README section **🔐 Website Auth & Generation**:

| Variable | Required | Purpose |
|----------|----------|---------|
| `GOOGLE_ANALYTICS_CLIENT_ID` | Yes | OAuth client ID from Google Cloud Console |
| `GOOGLE_ANALYTICS_CLIENT_SECRET` | Yes | OAuth client secret paired with the client ID |
| `GOOGLE_ANALYTICS_REDIRECT_URI` | No | Custom callback URI; defaults to `http://localhost:8080/oauth2callback` |
| `GOOGLE_ANALYTICS_REFRESH_TOKEN` | Output | **Generated by the flow** — add to Vercel as a Sensitive environment variable |

## Running the OAuth Helper

### Generate Your Refresh Token

```bash

# Run the interactive OAuth flow

npm run ga4:oauth

```

The script outputs:

```

Authorize this app by visiting this url:
https://accounts.google.com/o/oauth2/v2/auth?access_type=offline...

Paste callback URL or code:

```

### Complete the Flow

1. Open the printed URL in your browser
2. Sign in with a Google account that has **read access** to your GA4 property
3. Click **Allow** on the consent screen
4. Copy the full callback URL (e.g., `http://localhost:8080/oauth2callback?code=4/0AY0e-g5...`)
5. Paste it into the terminal prompt

The script extracts the refresh token and outputs:

```

Add this to Vercel Environment Variables as a Sensitive value:

GOOGLE_ANALYTICS_REFRESH_TOKEN=1//0gkU4B...

```

## Using the Refresh Token in Production

The `GOOGLE_ANALYTICS_REFRESH_TOKEN` enables server-side code to access GA4 data without user interaction. The repository's [`package.json`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/package.json) lists `@google-analytics/data` as a dependency (lines 22-23) for making API calls.

Example server-side usage pattern:

```javascript
import { BetaAnalyticsDataClient } from '@google-analytics/data';

const analyticsDataClient = new BetaAnalyticsDataClient({
  credentials: {
    client_id: process.env.GOOGLE_ANALYTICS_CLIENT_ID,
    client_secret: process.env.GOOGLE_ANALYTICS_CLIENT_SECRET,
    refresh_token: process.env.GOOGLE_ANALYTICS_REFRESH_TOKEN,
  },
});

async function getPageViews() {
  const [response] = await analyticsDataClient.runReport({
    property: `properties/${process.env.GA4_PROPERTY_ID}`,
    dateRanges: [{ startDate: '30daysAgo', endDate: 'today' }],
    dimensions: [{ name: 'pagePath' }],
    metrics: [{ name: 'screenPageViews' }],
  });
  return response;
}

```

The refresh token automatically generates short-lived access tokens for each API request.

## Key Implementation Files

| File | Lines | Purpose |
|------|-------|---------|
| `scripts/google-analytics-oauth.mjs` | 50-87 | Complete OAuth flow implementation |
| `.env.example` | 30-34 | Environment variable template |
| [`README.md`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/README.md) | 🔐 Website Auth & Generation section | High-level authentication documentation |
| [`package.json`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/package.json) | 22-23 | `google-auth-library` and `@google-analytics/data` dependencies |

## Summary

- **The GA4 OAuth flow** in `awesome-gpt-image-2` is implemented as a CLI helper at `scripts/google-analytics-oauth.mjs`
- **Offline access with forced consent** guarantees a refresh token is returned for server-side use
- **The `analytics.readonly` scope** provides read-only access to GA4 properties
- **Environment variables** `GOOGLE_ANALYTICS_CLIENT_ID` and `GOOGLE_ANALYTICS_CLIENT_SECRET` are required inputs
- **The output refresh token** must be stored as a **Sensitive** Vercel environment variable named `GOOGLE_ANALYTICS_REFRESH_TOKEN`
- **The `@google-analytics/data` client** consumes this token to query analytics without exposing credentials to browsers

## Frequently Asked Questions

### What OAuth scope does the repository request for GA4 access?

The repository requests **`https://www.googleapis.com/auth/analytics.readonly`** — the read-only scope for Google Analytics 4. This scope allows fetching reports and dimensions without permission to modify property settings or data. The scope is hardcoded in `scripts/google-analytics-oauth.mjs` lines 62-63.

### Why does the script use `prompt: 'consent'`?

The **`prompt: 'consent'`** parameter forces Google to display the consent screen even if the user previously authorized the application. This is essential for obtaining a **refresh token** every time, because Google only returns refresh tokens on the first authorization unless explicitly re-prompted. Without this parameter, repeat runs might fail to capture the refresh token needed for long-term server access.

### Can I use a service account instead of OAuth?

The repository's implementation is designed for **OAuth 2.0 with user credentials**, not service accounts. However, the `@google-analytics/data` client supports service account authentication using `client_email` and `private_key` instead of a refresh token. To migrate, create a service account in Google Cloud Console, grant it GA4 property access, and replace the credentials object in your server code accordingly.

### Where do I store the refresh token securely?

Store `GOOGLE_ANALYTICS_REFRESH_TOKEN` as a **Sensitive** environment variable in your deployment platform. In Vercel, navigate to **Project Settings → Environment Variables**, add the variable, and mark it as **Sensitive** to prevent exposure in logs or to team members without proper permissions. Never commit this token to version control.