I have a laravel project, where I try to get user's google analytics data. I created a OAuth2 authentication to get user's credentials. I also set a google cloud project, created a service account and added service account credentials file to my project properly (GOOGLE_APPLICATION_CREDENTIALS inside .env file for file path). Here is the code I try to get active users and new users.
use Google\Analytics\Data\V1beta\BetaAnalyticsDataClient;
use Google\Analytics\Data\V1beta\DateRange;
use Google\Analytics\Data\V1beta\Metric;
use Google\Analytics\Data\V1beta\Dimension;
use Illuminate\Support\Facades\Log;
public function getUserCounts(array $params): array
{
// Extract validated parameters.
$propertyId = $params['property_id'];
$startDate = $params['start_date'];
$endDate = $params['end_date'];
$dimension = strtolower($params['dimension']);
$dimensionName = $dimension === 'weekly' ? 'week' : 'date';
try {
// Initialize the BetaAnalyticsDataClient with the acquired access token.
$analyticsDataClient = new BetaAnalyticsDataClient();
// Set up the DateRange object.
$dateRange = new DateRange();
$dateRange->setStartDate($startDate);
$dateRange->setEndDate($endDate);
// Set up the Metric objects.
$usersMetric = new Metric();
$usersMetric->setName('activeUsers');
$newUsersMetric = new Metric();
$newUsersMetric->setName('newUsers');
// Set up the Dimension object.
$dimension = new Dimension();
$dimension->setName($dimensionName);
// Build the request data.
$requestData = [
'property' => $propertyId, // Must include the "properties/" prefix.
'dateRanges' => [$dateRange],
'metrics' => [$usersMetric, $newUsersMetric],
'dimensions' => [$dimension],
];
// Execute the report request.
$response = $analyticsDataClient->runReport($requestData);
// Process and return the response rows.
$result = [];
foreach ($response->getRows() as $row) {
$result[] = [
$dimension => $row->getDimensionValues()[0]->getValue(),
'users' => $row->getMetricValues()[0]->getValue(),
'newUsers' => $row->getMetricValues()[1]->getValue(),
];
}
return $result;
} catch (\Exception $e) {
Log::error('Error fetching GA4 user counts', ['error' => $e->getMessage()]);
return [
'error' => true,
'message' => 'Unable to fetch user counts from GA4',
];
}
}
Here is my problem. This approach works, only if user added my service account's email address to their google analytics users list. Otherwise I get
User does not have sufficient permissions for this property. To learn more about Property ID, see https://developers.google.com/analytics/devguides/reporting/data/v1/property-id
I don't want my user to manually add email address to their analytics dashboard. Is there another way to get permission? Maybe using user's access_token from OAuth2 or anything.
If you want to use Oauth2 then you can have the user run your application and authenticate it. You will get an access token and refresh token back. the access token will give you access to the api and the refresh token will give you access to request a new access token when that expires after an hour.
I dont have a sample for the data api but i do have one for the admin api it should be simlar more a matter of changing the api you request data from the scope should be the same i think and of couse the using should be changed to the data api.
<?php
require 'vendor/autoload.php';
use Google\Analytics\Admin\V1beta\AnalyticsAdminServiceClient;
putenv('GOOGLE_APPLICATION_CREDENTIALS=C:\YouTube\dev\credentials.json'); // Installed app credentials.
$credentials = getenv('GOOGLE_APPLICATION_CREDENTIALS');
$myfile = file_get_contents($credentials, "r") ;
$clientObj = json_decode($myfile);
// composer require google/analytics-admin
function getClient(): Client
{
$client = new AnalyticsAdminServiceClient();
$client->setApplicationName('Google analytics admin beta Oauth2');
$client->setScopes('https://www.googleapis.com/auth/analytics.readonly');
$client->setAuthConfig(getenv('GOOGLE_APPLICATION_CREDENTIALS'));
$client->setAccessType('offline');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = 'tokenAdmin.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
$client = getClient();
$tokenResponse = $client->getAccessToken();
print_r($tokenResponse);
print_r($tokenResponse["access_token"]);
$service = new AnalyticsAdminServiceClient( [
'credentials' => Google\ApiCore\CredentialsWrapper::build( [
'scopes' => [
'https://www.googleapis.com/auth/analytics',
'openid',
'https://www.googleapis.com/auth/analytics.readonly',
],
'keyFile' => [
'type' => 'authorized_user',
'client_id' => $clientObj->installed->client_id,
'client_secret' => $clientObj->installed->client_secret,
'refresh_token' => $tokenResponse["refresh_token"]
],
] ),
] );