• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » WordPress Development Recipe: Secure token-based API authentication for Google Analytics v4 REST in custom plugins

WordPress Development Recipe: Secure token-based API authentication for Google Analytics v4 REST in custom plugins

Prerequisites and Setup

This recipe assumes a foundational understanding of WordPress plugin development and familiarity with the Google Cloud Platform (GCP) console. We’ll be leveraging Google’s OAuth 2.0 service accounts for secure API access. Ensure you have a Google Cloud project set up and billing enabled, as API calls may incur costs.

The core of this authentication mechanism relies on a JSON Web Token (JWT) generated by a GCP service account. This token is then used to authenticate requests to the Google Analytics Data API v1.

Step 1: Create a GCP Service Account and Download Credentials

Navigate to the GCP Console, go to “IAM & Admin” > “Service Accounts”. Click “Create Service Account”. Give it a descriptive name (e.g., “wordpress-ga4-api-access”) and a unique ID. For the role, grant it the “Google Analytics Viewer” role (or a more granular role if your plugin only requires specific permissions). This is crucial for security; avoid granting excessive privileges.

After creation, click on the service account, go to the “Keys” tab, and click “Add Key” > “Create new key”. Select “JSON” as the key type and click “Create”. This will download a JSON file containing your service account’s private key and client email. Treat this file with extreme care; it’s equivalent to a password. Store it securely on your server, outside of your web-accessible directories.

The downloaded JSON file will look something like this:

{
  "type": "service_account",
  "project_id": "your-gcp-project-id",
  "private_key_id": "your-private-key-id",
  "private_key": "-----BEGIN PRIVATE KEY-----\nYOUR_PRIVATE_KEY_CONTENT\n-----END PRIVATE KEY-----\n",
  "client_email": "your-service-account-email@your-gcp-project-id.iam.gserviceaccount.com",
  "client_id": "your-client-id",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/your-service-account-email%40your-gcp-project-id.iam.gserviceaccount.com"
}

Step 2: Securely Store Service Account Credentials in WordPress

Never hardcode credentials directly into your plugin files. The most secure method is to store the JSON content in a WordPress option, encrypted. Alternatively, for higher security, store the file outside the webroot and load its contents using PHP’s file system functions, ensuring strict file permissions.

For this recipe, we’ll demonstrate storing the JSON content in a WordPress option. You’ll need to implement encryption/decryption. WordPress’s built-in `wp_salt` and `wp_hash_password` functions can be leveraged, or a more robust library like the `php-encryption` library by defuse. For simplicity here, we’ll show the conceptual storage and retrieval, assuming you’ll add robust encryption.

In your plugin’s settings page or activation hook, you might save the JSON content:

// Assuming $service_account_json_content is the raw JSON string from the downloaded file
$option_name = 'my_plugin_ga4_service_account_credentials';
$encrypted_credentials = encrypt_data( $service_account_json_content ); // Implement your encryption function
update_option( $option_name, $encrypted_credentials );

And retrieve it when needed:

function get_ga4_service_account_credentials() {
    $option_name = 'my_plugin_ga4_service_account_credentials';
    $encrypted_credentials = get_option( $option_name );

    if ( ! $encrypted_credentials ) {
        return false; // Or throw an exception
    }

    $decrypted_credentials = decrypt_data( $encrypted_credentials ); // Implement your decryption function
    return json_decode( $decrypted_credentials, true );
}

Step 3: Implement JWT Generation and Token Acquisition

Google’s service accounts use JWTs signed with their private key to obtain access tokens. We’ll need a PHP library to handle JWT creation and signing. The `firebase/php-jwt` library is a popular and robust choice.

Install it via Composer:

composer require firebase/php-jwt

Now, let’s create a function to generate the JWT and exchange it for an access token:

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

/**
 * Generates a JWT and exchanges it for a Google OAuth2 access token.
 *
 * @param array $service_account_credentials Service account credentials array.
 * @return string|false The access token on success, false on failure.
 */
function get_google_oauth2_access_token( array $service_account_credentials ) {
    $client_email = $service_account_credentials['client_email'];
    $private_key = $service_account_credentials['private_key'];
    $token_uri = 'https://oauth2.googleapis.com/token'; // Google's token endpoint

    // JWT Payload
    $payload = [
        'iss' => $client_email,
        'scope' => 'https://www.googleapis.com/auth/analytics.readonly', // Adjust scope as needed
        'aud' => 'https://oauth2.googleapis.com/token',
        'exp' => time() + 3600, // Token expires in 1 hour
        'iat' => time(),
    ];

    // Generate JWT
    $jwt = JWT::encode( $payload, $private_key, 'RS256' );

    // Request Access Token
    $request_body = [
        'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
        'assertion' => $jwt,
    ];

    $ch = curl_init();
    curl_setopt( $ch, CURLOPT_URL, $token_uri );
    curl_setopt( $ch, CURLOPT_POST, true );
    curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query( $request_body ) );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, true ); // Crucial for security

    $response = curl_exec( $ch );
    $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
    curl_close( $ch );

    if ( $http_code !== 200 ) {
        // Log error: $response contains error details
        error_log( "Google OAuth2 Token Error: " . $response );
        return false;
    }

    $token_data = json_decode( $response, true );

    if ( isset( $token_data['access_token'] ) ) {
        return $token_data['access_token'];
    } else {
        // Log error: Missing access_token in response
        error_log( "Google OAuth2 Token Response Error: Missing access_token. Response: " . $response );
        return false;
    }
}

Step 4: Make Authenticated Requests to the Google Analytics Data API v1

With a valid access token, you can now query the Google Analytics Data API v1. The API endpoint for the `runReport` method is:

https://analyticsdata.googleapis.com/v1beta/properties/YOUR_PROPERTY_ID:runReport

Replace YOUR_PROPERTY_ID with your actual Google Analytics 4 Property ID.

Here’s an example of how to construct and send a request:

/**
 * Fetches a report from the Google Analytics Data API v1.
 *
 * @param string $property_id Your GA4 Property ID.
 * @param array $report_request The API request body for runReport.
 * @return array|false The report data on success, false on failure.
 */
function get_ga4_report( string $property_id, array $report_request ) {
    $credentials = get_ga4_service_account_credentials();
    if ( ! $credentials ) {
        error_log( "GA4 API: Failed to retrieve service account credentials." );
        return false;
    }

    $access_token = get_google_oauth2_access_token( $credentials );
    if ( ! $access_token ) {
        error_log( "GA4 API: Failed to obtain access token." );
        return false;
    }

    $api_url = "https://analyticsdata.googleapis.com/v1beta/properties/{$property_id}:runReport";

    $headers = [
        'Authorization: Bearer ' . $access_token,
        'Content-Type: application/json',
    ];

    $ch = curl_init();
    curl_setopt( $ch, CURLOPT_URL, $api_url );
    curl_setopt( $ch, CURLOPT_POST, true );
    curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $report_request ) );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, true ); // Crucial for security

    $response = curl_exec( $ch );
    $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
    curl_close( $ch );

    if ( $http_code !== 200 ) {
        error_log( "GA4 API Error (HTTP {$http_code}): " . $response );
        return false;
    }

    return json_decode( $response, true );
}

// Example Usage:
$ga4_property_id = 'YOUR_PROPERTY_ID'; // Replace with your GA4 Property ID

$report_params = [
    'dateRanges' => [
        [
            'startDate' => '2023-01-01',
            'endDate' => 'today',
        ],
    ],
    'dimensions' => [
        ['name' => 'date'],
        ['name' => 'country'],
    ],
    'metrics' => [
        ['name' => 'activeUsers'],
        ['name' => 'sessions'],
    ],
    'limit' => 1000, // Example limit
];

$report_data = get_ga4_report( $ga4_property_id, $report_params );

if ( $report_data ) {
    // Process $report_data
    echo '<pre>';
    print_r( $report_data );
    echo '</pre>';
} else {
    echo 'Failed to retrieve GA4 report.';
}

Security Considerations and Best Practices

Credential Security: The service account JSON file is highly sensitive. Store it outside the webroot and restrict file permissions to the absolute minimum required. If storing in WordPress options, ensure robust encryption and decryption are implemented. Regularly rotate service account keys.

Least Privilege: Grant the service account only the necessary permissions (e.g., “Google Analytics Viewer”) in GCP. Avoid using broad roles like “Editor” or “Owner”.

Scope Management: Explicitly define the OAuth scopes required in the JWT payload. The example uses https://www.googleapis.com/auth/analytics.readonly, which is appropriate for read-only access. Adjust if your plugin needs to write data (though the Data API v1 is primarily for reading).

Error Handling and Logging: Implement comprehensive error handling for API requests and token generation. Log errors to a secure location (e.g., WordPress debug log) for troubleshooting. Never expose sensitive error details to the end-user.

Token Expiration and Refresh: The access tokens obtained are short-lived (typically 1 hour). The JWT itself is also time-bound. The `get_google_oauth2_access_token` function handles re-authentication on each call. For high-frequency access, consider caching the access token and its expiration time, but be mindful of security implications and potential staleness.

API Versioning: Always be aware of the API version you are using (e.g., `v1beta`). Google may deprecate older versions. Keep your plugin updated to use the latest stable API versions.

Rate Limiting: Be aware of Google Analytics API rate limits. Implement backoff strategies or caching if your plugin makes a high volume of requests.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Leveraging PHP 8 JIT and AWS Lambda for High-Performance, Serverless WordPress REST API Backends
  • Beyond the Basics: Leveraging PHP 8.3’s JIT Compiler and Fibers for High-Concurrency Laravel Applications
  • Zero-Downtime Deployments with Docker, Laravel, and AWS ECS: A Deep Dive into Blue/Green Strategies
  • Leveraging PHP 9’s JIT and Concurrency Features for High-Performance Laravel Microservices on AWS ECS
  • Leveraging PHP 8.3 JIT and OPcache for Sub-Millisecond API Response Times: A Practical Deep Dive

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (664)
  • Desktop Applications (14)
  • DevOps (11)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (6)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (873)
  • PHP (14)
  • PHP Development (49)
  • Plugins & Themes (244)
  • Programming Languages (10)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (650)
  • SEO & Growth (492)
  • Server (118)
  • Softwares (1)
  • Ubuntu (9)
  • Uncategorized (17)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (24)
  • WordPress Plugin Development (728)
  • WordPress Theme Development (357)

Recent Posts

  • Leveraging PHP 8 JIT and AWS Lambda for High-Performance, Serverless WordPress REST API Backends
  • Beyond the Basics: Leveraging PHP 8.3's JIT Compiler and Fibers for High-Concurrency Laravel Applications
  • Zero-Downtime Deployments with Docker, Laravel, and AWS ECS: A Deep Dive into Blue/Green Strategies

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • WordPress Plugin Development (728)
  • Debugging & Troubleshooting (664)
  • Security & Compliance (650)
  • SEO & Growth (492)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala