Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Analytics-Data] Oauth2 Authentication #3975

Closed
jdpedrie opened this issue Apr 21, 2021 · 4 comments
Closed

[Analytics-Data] Oauth2 Authentication #3975

jdpedrie opened this issue Apr 21, 2021 · 4 comments
Labels
type: question Request for information or clarification. Not an issue.

Comments

@jdpedrie
Copy link
Contributor

Hi,

We can use already authorised oAuth2 credentials for Analytics Reporting API v4 or v3 with this GA4 property data fetch?

Or we need to use service account strictly?

Thanks,


Moved from https://github.com/googleapis/php-analytics-data/issues/4

cc @chiragvels

@jdpedrie jdpedrie added the type: question Request for information or clarification. Not an issue. label Apr 21, 2021
@Mark-H
Copy link

Mark-H commented Apr 22, 2021

The following seems to work for oAuth authentication wise:

        $client = new \Google\Analytics\Data\V1beta\BetaAnalyticsDataClient([
            'credentials' => \Google\ApiCore\CredentialsWrapper::build([
                'keyFile' => [
                    'type' => 'authorized_user',
                    'client_id' => $clientId),
                    'client_secret' => $clientSecret,
                    'access_token' => $accessToken,
                    'refresh_token' => $refreshToken,
                    'scopes' => \Google\Analytics\Data\V1beta\BetaAnalyticsDataClient::$serviceScopes,
                ]
            ])
        ]);

Still working on getting the first report to show up though...

Some docs here as well: #3975

@chiragvels
Copy link

Hi @Mark-H ,

Ok, Let me try when time permits.

May be you have pointed to same #3975 issue. Or Is there any docs as well?

Thanks,

@Mark-H
Copy link

Mark-H commented Apr 23, 2021

Whoops - meant to link to this PR instead: googleapis/php-analytics-data#2. Long night. ;) Note that link is for the v1alpha which is slightly different, most notably that sample provides an Entity object while v1beta expects a property in the format properties/123456789.

Here's a sample (app-specifics redacted) of how I got it working. And yes, v3 oauth tokens are valid for v4 too.

First creating an OAuth2 instance with as much relevant tokens as are available that'll be used in different places:

use Google\Auth\OAuth2;

public function getOAuth2 () 
{
        $this->OAuth2 = new OAuth2([
            'scope' => 'https://www.googleapis.com/auth/analytics.readonly',
            'tokenCredentialUri' => 'https://oauth2.googleapis.com/token',
            'authorizationUri' => 'https://accounts.google.com/o/oauth2/auth',
            'redirectUri' => 'urn:ietf:wg:oauth:2.0:oob', // I'm using "offline"/"out of band" oauth, so this asks the user to copy/paste the auth code back into the app, this may also be a full whitelisted URL to redirect back to your oauth login 
            'clientId' => $clientId,
            'clientSecret' => $clientSecret,
        ]);

        // If the scope doesn't already have the refresh token, but we do have it, set it
        if (!$this->OAuth2->getRefreshToken()) {
            $refreshToken = ... get from storage ...;
            if (!empty($refreshToken)) {
                $this->OAuth2->setRefreshToken($refreshToken);
            }
        }

        // If the scope doesn't already have the access token...
        if (!$this->OAuth2->getAccessToken()) {
            // If we have it in cache and it's the right (array) format, set it
            $accessToken = ... get access token from storage...
            if (is_array($accessToken)
                && array_key_exists('access_token', $accessToken)
                && !empty($accessToken['access_token'])
            ) {
                $this->OAuth2->updateToken($accessToken);
            } 
            // If we don't have the access token, but we do have a refresh token, fetch a new auth token
            elseif ($this->OAuth2->getRefreshToken()) {
                $accessToken = $this->OAuth2->fetchAuthToken();
                // ... write it to cache or session or something ...
            }
        }
        return $this->OAuth2;
}

(I've also seen some mention of the OAuth client having built-in storage, but I'm handling it outside of that.)

And using those credentials for running a minimal report and parsing that into a somewhat usable associative array structure dumped to screen:

use Google\Analytics\Data\V1beta\BetaAnalyticsDataClient;

public function run()
{
        $oauth = $this->getOAuth2();
        $client = new BetaAnalyticsDataClient(['credentials' => $oauth]);
        $property = '123456789';

        $response = $client->runReport([
            'property' => 'properties/' . $property,
            'dateRanges' => [
                new DateRange([
                    'start_date' => '28daysAgo',
                    'end_date' => 'today',
                ]),
            ],
            'dimensions' => [
                new Dimension([
                    'name' => 'date',
                ])
            ],
            'metrics' => [
                new Metric([
                    'name' => 'sessions',
                ]),
                new Metric([
                    'name' => 'screenPageViews',
                ]),
            ],
            'orderBys' => [
                new OrderBy([
                    'dimension' => new DimensionOrderBy([
                        'dimension_name' => 'date'
                    ])
                ])
            ]
        ]);

        $metricHeaders = [];
        $dimensionHeaders = [];
        $data = [];

        /** @var \Google\Analytics\Data\V1beta\MetricHeader $metricHeader */
        foreach ($response->getMetricHeaders() as $metricHeader) {
            $metricHeaders[] = $metricHeader->getName();
        }

        /** @var \Google\Analytics\Data\V1beta\DimensionHeader $dimensionHeader */
        foreach ($response->getDimensionHeaders() as $dimensionHeader) {
            $dimensionHeaders[] = $dimensionHeader->getName();
        }

        /** @var \Google\Analytics\Data\V1beta\Row $row */
        foreach ($response->getRows() as $row) {
            $rowData = [];

            /** @var \Google\Analytics\Data\V1beta\DimensionValue $dimensionValue */
            foreach ($row->getDimensionValues() as $idx => $dimensionValue) {
                $rowData[$dimensionHeaders[$idx]] = $dimensionValue->getValue();
                print $dimensionValue->getValue() . ' => ';
            }

            /** @var \Google\Analytics\Data\V1beta\MetricValue $metricValue */
            
            foreach ($row->getMetricValues() as $idx => $metricValue) {
                $rowData[$metricHeaders[$idx]] = $metricValue->getValue();
                print '[' . $metricHeaders[$idx] . '] ' . $metricValue->getValue() . ' ';
            }

            $data[] = $rowData;
            print PHP_EOL;
        }

        var_dump($data);
}

As my use case lets a user choose the profile to show reports for, it's also using the google/analytics-admin package to access accounts and GA4 properties. This re-uses the same OAuth2 object and doesn't require other scopes or anything.

        $admin = new AnalyticsAdminServiceClient(['credentials' => $oauth]);

        /** @var \Google\Analytics\Admin\V1alpha\AccountSummary[] $summaries */
        $summaries = $admin->listAccountSummaries();
        foreach ($summaries as $summary) {
            print $summary->getDisplayName() . PHP_EOL;

            /** @var \Google\Analytics\Admin\V1alpha\PropertySummary[] $properties */
            $properties = $summary->getPropertySummaries();
            foreach ($properties as $property) {
                $propNum = substr($property->getProperty(), strlen('properties/'));
                print "\t- " . $property->getDisplayName() . ' [' . $propNum . ']' . PHP_EOL;
            }
        }

I haven't finished upgrading the authorization step yet (this is an upgrade from the previous API so I already had a valid refresh_token to use and it was really late last night), but from what I've gathered so far that'll start with creating the auth url:

$oauth = $this->getOAuth2();
$authUrl = $oauth->buildFullAuthorizationUri();
// ... redirect to authUrl/open in a popup from the client side

Then providing the code to the oauth object and calling fetchAuthToken which will exchange the code for a refresh and access token available via their setters to persist somewhere in the app.

$code = $_POST['code'];
$oauth = $this->getOAuth2();
$oauth->setCode($code);
$credentials = $oauth->fetchAuthToken();
// save $credentials['access_token'] and $credentials['refresh_token'] somewhere in the app

I have no idea if this is all the officially recommended approach as the documentation is spotty at best, but it does seem to work.

@vishwarajanand
Copy link
Contributor

I think this is answered now. closing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question Request for information or clarification. Not an issue.
Projects
None yet
Development

No branches or pull requests

4 participants