I am trying to create a web-app with a simple dashboard with Analytics data for the accounts who logged in with Google. I am using Laravel with Socialite package, and I can log the users in with Google currently. I have my developer client-key and client-secret. I set scopes for Analytics read-only, and offline access, and I store customer name, email, Google ID, access token and refresh token in my database. I can log the user in without any problem.
What I want to do is for now, just access the profiles an Analytics account currently has. I followed Analytics API documentation examples but could not get it working. Since I am storing an access token and a refresh token, I think I should be able to authenticate the current user and get their Analytics data, but I could not find any simple methods from the Client and Analytics libraries. I will need to access their Analytics data offline, and this is why I think I should be able to authorize my requests with access token and refresh token, but I do not get any Analytics specific data from user login process. I am completely lost now, how do I authorize my requests to Anayltics API? I have been using AdWords API for more than 8 months, and everything is crystal clear in AdWords API documentation, but I could not get anything working with Analytics API.
These are my user login methods:
public function redirectToProvider()
{
$parameters = ['access_type' => 'offline'];
return Socialite::driver('google')
->scopes(['https://www.googleapis.com/auth/analytics.readonly'])
->with($parameters)
->redirect();
}
/**
* Obtain the user information from Google.
*
* #return Response
*/
public function handleProviderCallback()
{
$outsiderLogin = Socialite::driver('google')->stateless()->user();
$user = User::where('googleID', $outsiderLogin->id)->first();
// Register the user if there is no user with that id.
if (!$user) {
$user = new User;
$user->name = $outsiderLogin->name;
$user->googleID = $outsiderLogin->id;
$user->email = $outsiderLogin->email;
$user->token = $outsiderLogin->token;
$user->refreshToken = $outsiderLogin->refreshToken;
$user->save();
}
// Log the user in.
Auth::login($user);
return redirect('/home');
}
Thank you very much.
I have found the solution for now. At first, I figured that I needed the code that returns with authentication URL from Google, and when I inspect the Socialite package, I have found a protected method getCode() in \vendor\laravel\socialite\src\Two\AbstractProvider.php, which returns the code from the URL. I edited the source file of the package and changed the method type from protected to public, and that made it possible to use that method outside of the class, which allowed me to access the code from the URL, then store it in DB for further authentication requirements. But there were issues with this setup, first of all, I should find a way to keep that package without any update, since any update will rollback the changes I made to the source file. The second problem I faced was the way I store tokens. By default, Google Client API returns an array which contains the fields access_token, refresh_token, expires_in, id and created, and with these fields, it authenticates the requests to Analytics server. In my scenario, there were no standard array returning from the basic Socialite login process. There were access_token, refresh_token and expires variables and I stored them all in my database as well. This caused an issue with Google library, it asked for a structured array and I did not even have the variables expires_in and created, this is why I setup a fake array which tells Google to refresh token with every request, and this was not a good practice either.
At the end, I could not understand how to use any package online and I wrote my own simple authentication, and I do not know if it has any vulnerabilities, but it works for me, it may also work for those who needs it.
Here are my routes lines:
Route::get('auth/google', [
'as' => 'googleLogin',
'uses' => 'Auth\AuthController#redirectToProvider'
]);
Route::get('auth/google/callback', [
'as' => 'googleLoginCallback',
'uses' => 'Auth\AuthController#handleProviderCallback'
]);
And these are the AuthController methods:
/**
* Redirect the user to the Google authentication
*/
public function redirectToProvider()
{
// Create the client object and set the authorization configuration from JSON file.
$client = new Google_Client();
$client->setAuthConfig('/home/vagrant/Analytics/client_secret.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/auth/google/callback');
$client->addScope(Google_Service_Analytics::ANALYTICS_READONLY);
$client->addScope("email");
$client->addScope("profile");
$client->setAccessType("offline");
$auth_url = $client->createAuthUrl();
return redirect($auth_url);
}
/**
* Obtain the user information from Google.
*
* #return redirect to the app.
*/
public function handleProviderCallback()
{
// Handle authorization flow from the server.
if (! isset($_GET['code'])) {
return redirect('auth/google');
} else {
// Authenticate the client, and get required informations.
$client = new Google_Client();
$client->setAuthConfig('/home/vagrant/Analytics/client_secret.json');
$client->authenticate($_GET['code']);
// Store the tokens in the session.
Session::put('token', $client->getAccessToken());
$service = new Google_Service_Oauth2($client);
$userInfo = $service->userinfo->get();
$user = User::where('googleID', $userInfo->id)->first();
// If no match, register the user.
if(!$user) {
$user = new User;
$user->name = $userInfo->name;
$user->googleID = $userInfo->id;
$user->email = $userInfo->email;
$user->refreshToken = $client->getRefreshToken();
$user->code = $_GET['code'];
$user->save();
}
Auth::login($user);
return redirect('/home');
}
}
I have placed the client_secret.json file I have downloaded from Google API Console into the specified folder, this may be different for you. I have also modified the migration file in order to match the required segemnts. After these steps, I am able treat that user as it is a simple user that registered with the basic Laravel auth.
Now I can query, say, the accounts in the user's Google Analytics account like this:
/**
* #var $client to be authorized by Google.
*/
private $client;
/**
* #var $analytics Analytics object to be used.
*/
private $analytics;
public function __construct()
{
$this->client = $this->AuthenticateCurrentClient();
$this->analytics = new Google_Service_Analytics($this->client);
}
private function AuthenticateCurrentClient(){
$user = Auth::user();
$token = Session::get('token');
// Authenticate the client.
$client = new Google_Client();
$client->setAccessToken($token);
$client->authenticate($user->code);
return $client;
}
public function GetAccounts(){
try {
$accountsObject = $this->analytics->management_accounts->listManagementAccounts();
$accounts = $accountsObject->getItems();
return $accounts;
} catch (apiServiceException $e) {
print 'There was an Analytics API service error '
. $e->getCode() . ':' . $e->getMessage();
} catch (apiException $e) {
print 'There was a general API error '
. $e->getCode() . ':' . $e->getMessage();
}
}
There were thousands of times Stack Overflow has helped me, I hope this helps someone to get things working.
You're not really going to find what you're looking for with the Socialite package shipped with Laravel (which is more used for logins and that's about it).
You can however find many Google Analytic packages (along with many other Laravel bundles) here which should help you make API calls:
http://packalyst.com/s/google%20analytics
More specifically, this package: https://github.com/spatie/laravel-analytics
That, or run your own Guzzle and cURL scripts. I use Guzzle when I need something quick without building a full blown API.
However, there's an interesting post here about using Socialite to access GA data. But you're quite limited. If you're creating user driven dashboards, I'd opt for a separate package.
https://laracasts.com/discuss/channels/tips/how-i-made-google-analytics-work-with-socialite
I'am also trying to do the same thing. By far, I've user authentication at place with oAuth 2.0 and Socialite package. I need the list of sites to be fetched from GA. And am totally stuck there. It would really be great if you can guide me as on how should I move on further..
Related
My application is meant to interact with the user's google calendar. So I set up all the GoogleOAuth stuff in the Google APIs Console and in laravel with the google/apiclient package via composer.
GoogleCalendarApi.php
public function __construct($redirectUri, $tokens = [], $state = '')
{
$this->client = new \Google_Client();
$this->client->setAuthConfig([
'client_id' => env('GOOGLE_CALENDAR_API_OAUTH_CLIENT_ID'),
'client_secret' => env('GOOGLE_CALENDAR_API_OAUTH_CLIENT_SECRET'),
]);
$this->client->addScope(\Google_Service_Calendar::CALENDAR);
$this->client->setRedirectUri($redirectUri);
$this->client->setAccessType('offline');
if (! empty($tokens) && ! isset($tokens['error'])) {
$this->client->setAccessToken($tokens);
}
if (! empty($state)) {
$this->client->setState($state);
}
$this->calendar = new \Google_Service_Calendar($this->client);
}
The $redirectUri is an internal url that's supposed to get the code and save it for the user to access the Calendar API when necessary.
GoogleCalendarApiController.php
public function oAuthRedirectTarget(Request $request)
{
$code = $request->get('code');
$user = Auth::user();
$instance = GoogleCalendarApi::getInstanceForUser($user);
$tokens = $instance->client->fetchAccessTokenWithAuthCode($code);
$user->setMeta('google_calendar_api_tokens', json_encode($tokens));
return redirect(route('account.user'));
}
But the oAuthRedirectTarget function is only executed when I do not protect it with the auth guard, because as soon as the GoogleOAuth process redirects me to my application I am logged out.
Since I need the current user (which I don't have access to in a guest route) to save the tokens for them, I created a custom Guard that authenticates the user based on a temporary remember_token-like string. That did actually work and the tokens were saved to the current user, but since the route behind this line return redirect(route('account.user')); is protected by the default auth guard, I get redirected to the login screen.
I tried adding a wrapper around the OAuth url created with the package and using the internal redirect function to get there, and saving the session before redirecting, and even logging the user in manually again with the "remember" option set to true, but I keep getting logged out on return from Google.
GoogleCalenderApiController.php
public function oAuthCodeUrlWrapper(Request $request)
{
$user = Auth::user();
$url = GoogleCalendarApi::getUserOAuthCodeUrl($user);
$request->session()->save(); # doesn't help
Auth::login($user, true); # doesn't help either
return redirect($url);
}
I tried setting the SESSION_DRIVER to cookie instead of file, but that gave me a Bad Gateway error first (maybe a VM hiccup) and after some more fiddling I forgot, I was back to just being logged out.
So, all that forms the question:
How do I retain the current user's session after redirecting away to GoogleOAuth and coming back from there?
I'm building an application that has a simple email-pass login. I want to use the Microsoft Graph to return the profile information of a user that has logged in (I'm not using office365 OAuth right now).
I've set up an action that tries to fetch this user data (i.e given name or profile photo) from an organization I'm part of. I got this error:
"line":113,
"message":"Client error: GET https://graph.microsoft.com/v1.0/users/{user}
resulted in a 401 Unauthorized response:
{
"error": {
"code": "Authorization_IdentityNotFound",
"message": "The identity of the calling application (truncated...)"
}
}
I've set up the application as specified on the Microsoft Graph tutorial (step 2) and this the code I've written, using the repository readme:
class MsGraphService implements iAction
{
protected $accessToken;
public function __construct()
{
$guzzle = new \GuzzleHttp\Client();
$url = 'https://login.microsoftonline.com/'.ENV('MSGRAPH_TENAND_ID').'/oauth2/token?api-version=1.0';
$token = json_decode($guzzle->post($url, [
'form_params' => [
'client_id' => ENV('MSGRAPH_CLIENT_ID'),
'client_secret' => ENV('MSGRAHP_CLIENT_SECRET'),
'resource' => 'https://graph.microsoft.com/',
'grant_type' => 'client_credentials',
],
])->getBody()->getContents());
$this->accessToken = $token->access_token;
}
public function execute($data)
{
$graph = new Graph();
$graph->setAccessToken($this->accessToken);
$user = $graph->createRequest("GET", sprintf("/users/%s",$data['email']))
->setReturnType(Model\User::class)
->execute();
return $user->getGivenName();
}
}
The accesstoken attribute was caught, so I think the app configuration is ok.
Maybe is an issue about API permissions, specifically: Application permissions?
I've also asked about this on GitHub.
You are probably running into problems because you are trying to use the AzureAD V1 authentication endpoint. It is much easier to get the AzureAD v2 endpoint to work with Microsoft Graph. There are details on how to do it here https://learn.microsoft.com/en-us/graph/tutorials/php?tutorial-step=3
The main different between V1 and V2 is that you no longer use a resource parameter and you use scopes instead to say want you want access to.
When trying to fetch user related information using the API (no signed-in user) you should ask an administrator for permissions to read that information. In order to set the correct permissions to your application you should:
Go to https://portal.azure.com and login
Click on Azure Active Directory
Go to App Registrations
Select your existing appliaction and click on View API permissions
Select + Add a permission / Microsoft Graph / Application Permissions / User / User.Read.All
Ask an administrator for consent
With that, you will be able to fetch users data from your application without a signed-in user.
It's been such a major headache getting my service account to authenticate on the same webapp where I have users logging in via oauth2 as well.
So I'm wondering, is this even possible?
If not, should one just stick with the service account? Does one have to then authenticate the users on one's own - old school style? Haha
Thanks.
Regarding the service account, I have enabled the domain wide delegation, enabled the client key + api scope in my G suite admin console, and have gotten the php sample with the books api working. However any time I try any other api, other than books, I get the error,
client is unauthorized to retrieve access tokens using this method
UPDATE: I've tried to use #dalmto's example, and have added a few lines to test the gmail api, for example:
putenv('GOOGLE_APPLICATION_CREDENTIALS=credentials.json');
$user = 'email#domain.de';
function getGoogleClient() {
return getServiceAccountClient();
}
function getServiceAccountClient() {
try {
// Create and configure a new client object.
$client2 = new Google_Client();
$client2->useApplicationDefaultCredentials();
$client2->setScopes(array('https://www.googleapis.com/auth/userinfo.email','https://www.googleapis.com/auth/admin.directory.user.readonly','https://www.googleapis.com/auth/userinfo.profile','https://www.googleapis.com/auth/gmail.readonly','https://www.googleapis.com/auth/calendar'));
$client2->setAccessType('offline');
$client2->setSubject($user);
return $client2;
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
}
$newGoogleClient = getGoogleClient();
$service3 = new Google_Service_Gmail($newGoogleClient);
$results3 = $service3->users_labels->listUsersLabels($user);
But am now just receiving "400: Bad Request" errors
EDIT: After some more digging there is a note: 'failedPrecondition' - any idea which precondition that could be? I've allowed the following scopes for the client in my admin console:
hxxps://www.googleapis.com/auth/gmail.metadata,
hxxps://www.googleapis.com/auth/userinfo.email,
hxxps://www.googleapis.com/auth/userinfo.profile,
hxxps://www.googleapis.com/auth/gmail.modify,
hxxps://www.googleapis.com/auth/gmail.readonly,
hxxps://www.googleapis.com/auth/gmail.labels,
hxxps://mail.google.com/
And enabled the apis and enabled the scope in the 'OAuth Consent Screen'
DWD is also enabled: Service Account Overview Screenshot
EDIT2: Okay so I found the missing precondition was the "setSubject".
Once I added that it went a step further, but still failed again at '"error": "unauthorized_client",\n "error_description": "Client is unauthorized to retrieve access tokens using this method.'
FYI: When creating the service account, I gave it the "project -> owner" role. Is that sufficient? Does one have to add more?
EDIT3: I've also just checked logger and it says that DWD is enabled.. Im at my whits end here haha
client: {
adminState: {
updateTime: "2018-11-23T00:29:44.810Z"
}
assertionMatchExistingGrant: "MATCH_GRANT_DISABLED"
authType: "PUBLIC_KEY"
brandId: "aaaaaaaaaaaaaa"
clientId: "aaaaaaaaaaaaaaaaaa"
consistencyToken: "2018-11-23T00:29:44.953175Z"
creationTime: "2018-11-23T00:29:44.810Z"
displayName: "Client for servicemaint1"
domainWideDelegation: "DELEGATION_ENABLED"
projectNumber: "aaaaaaaaaaaaaaaa"
threeLeggedOauth: "DISABLED"
updateTime: "2018-11-23T00:29:44.953175Z"
}
EDIT4: FINALLY WORKING!
So I had been trying this in a new project I created for testing all morning / last night. But my oauth2 user authenticating was running through a different project (where I also couldn't get the service account working all of yesterday morning / afternoon).
So anyway, I noticed in: https://myaccount.google.com/permissions "Apps with Access to your account" - only my old project / app was authorized. So I switched back to my first project, created a new service account client ID .json file and it finallyyy worked to authenticate both! :)
I must have that authorized that somewhere extra along the line which I had not done with the second project.
Thanks again.
EDIT5: One more quick question - is this the correct way to do this on stackoverflow? With constantly going back to edit?
Also for others stumbling upon this later, here's my total authentication block (sorry its a bit long):
putenv('GOOGLE_APPLICATION_CREDENTIALS=maintenanceapp.json');
$user = 'xyz#abc.com';
function getGoogleClient() {
return getServiceAccountClient();
}
function getServiceAccountClient() {
$user = 'xyz#abc.com';
try {
// Create and configure a new client object.
$client2 = new Google_Client();
$client2->useApplicationDefaultCredentials();
$client2->setScopes(['https://www.googleapis.com/auth/gmail.metadata','https://www.googleapis.com/auth/userinfo.email','https://www.googleapis.com/auth/userinfo.profile','https://www.googleapis.com/auth/gmail.modify','https://www.googleapis.com/auth/gmail.readonly','https://www.googleapis.com/auth/gmail.labels']);
//$client2->setAccessType('offline');
$client2->setSubject($user);
return $client2;
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage();
}
}
$newGoogleClient = getGoogleClient();
$service3 = new Google_Service_Gmail($newGoogleClient);
$results3 = $service3->users_labels->listUsersLabels($user);
/*************************************************
* Ensure you've downloaded your oauth credentials
************************************************/
if (!$oauth_credentials = getOAuthCredentialsFile()) {
echo missingOAuth2CredentialsWarning();
return;
}
/************************************************
* NOTICE:
* The redirect URI is to the current page, e.g:
* http://localhost:8080/idtoken.php
************************************************/
$redirect_uri = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$client = new Google_Client();
// USER AUTH
$client->setAuthConfig($oauth_credentials);
$client->setRedirectUri($redirect_uri);
$client->setScopes(array('https://www.googleapis.com/auth/userinfo.email','https://www.googleapis.com/auth/userinfo.profile','https://www.googleapis.com/auth/gmail.readonly','https://www.googleapis.com/auth/calendar'));
$client->setApprovalPrompt('auto');
$client->setAccessType('offline');
$plus = new Google_Service_Plus($client);
/************************************************
* If we're logging out we just need to clear our
* local access token in this case
************************************************/
if (isset($_REQUEST['logout'])) {
unset($_SESSION['id_token_token']);
}
/************************************************
* If we have a code back from the OAuth 2.0 flow,
* we need to exchange that with the
* Google_Client::fetchAccessTokenWithAuthCode()
* function. We store the resultant access token
* bundle in the session, and redirect to ourself.
************************************************/
if (isset($_GET['code'])) {
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
// store in the session also
$_SESSION['id_token_token'] = $token;
// redirect back to the example
header('Location: https://abc.de/index.php');
// return;
}
/************************************************
If we have an access token, we can make
requests, else we generate an authentication URL.
************************************************/
if (
!empty($_SESSION['id_token_token'])
&& isset($_SESSION['id_token_token']['id_token'])
) {
$client->setAccessToken($_SESSION['id_token_token']);
} else {
$authUrl = $client->createAuthUrl();
//header('Location: ' . $authUrl);
}
/************************************************
If we're signed in we can go ahead and retrieve
the ID token, which is part of the bundle of
data that is exchange in the authenticate step
- we only need to do a network call if we have
to retrieve the Google certificate to verify it,
and that can be cached.
************************************************/
if ($client->getAccessToken()) {
$token_data = $client->verifyIdToken();
}
In google developer console when you create your project and the credentials you must choose which type of client you are going to create for which type of application.
There are several different ways to authenticate to google.
OAuth2 native
OAuth2 web
Mobile
Service account
The code to use these clients is also different. You cant create a web OAuth2 client and use it for the code meant to be calling a service account.
"client is unauthorized to retrieve access tokens using this method".
Means exactly that. The client you have set up on Google developer console is either not a service account client or the code you are using is not meant for a service account client.
This is my serviceaccount.php sample. If your code needs to look something like this and you need to make sure that the client you created on the google developer console is a service account client.
require_once __DIR__ . '/vendor/autoload.php';
// Use the developers console and download your service account
// credentials in JSON format. Place the file in this directory or
// change the key file location if necessary.
putenv('GOOGLE_APPLICATION_CREDENTIALS='.__DIR__.'/service-account.json');
/**
* Gets the Google client refreshing auth if needed.
* Documentation: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
* Initializes a client object.
* #return A google client object.
*/
function getGoogleClient() {
return getServiceAccountClient();
}
/**
* Builds the Google client object.
* Documentation: https://developers.google.com/api-client-library/php/auth/service-accounts
* Scopes will need to be changed depending upon the API's being accessed.
* array(Google_Service_Analytics::ANALYTICS_READONLY, Google_Service_Analytics::ANALYTICS)
* List of Google Scopes: https://developers.google.com/identity/protocols/googlescopes
* #return A google client object.
*/
function getServiceAccountClient() {
try {
// Create and configure a new client object.
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope([YOUR SCOPES HERE]);
return $client;
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
}
Developer console
Under clients check that the client you are using is one that can be found under service account keys. If not then it is the wrong client type and will not work with your code. Create a new service account client and set up domain wide delegation with that client id.
response_type=code
client_id=348268306866-9dl0kdgn2f9bjhoge7pris1jo8u9si47.apps.googleusercontent.com
redirect_uri=https://degoo.com/me/googleoauth2callback
access_type=offline
scope=https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/contacts.readonly
state={"RedirectUrl":"/me/chooseaccount","RegisterIfNotExists":true}
That’s all we know.
I am trying to get my web application to use a dedicated email account to upload files to google drive through the API. The problem being that it keeps requiring the potential users to authenticate the account that I prepared for it, which is exactly what I am trying to prevent the need for.
Any solutions I came across directed me at using the refresh token to ensure the account stays authorized. This still comes with the issue that on the first time use, an user has to authenticate said account. Saving the access token with the refresh token locally didn't work, as it still forced new users to authenticate.
the snippet below displays the code used to authenticate.
class AccessDrive
{
private $client;
//initialize the client data provided on the call of the function
private function initClient($scopes, $clientsecret, $returnUrl)
{
$client = new Google_Client();
try{$client->setAuthConfig($clientsecret);}catch(Google_Exception $e){echo $e;}
$client->setClientId(CLIENT_ID);
$client->setApplicationName(APPLICATION_NAME);
$client->setAccessType('offline');
//$client->setApprovalPrompt('force');
$client->setIncludeGrantedScopes(true); // incremental auth
foreach ($scopes as $scope) {
$client->addScope($this->getScope($scope)); //assume one or multiple from the google drive scopes
}
$client->setRedirectUri( $returnUrl);
return $client;
}
public function Login()
{
if (!$_SESSION['code']) {
$auth_url = $this->client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
error_reporting(-1 & ~E_WARNING);
$this->client->authenticate($_SESSION['code']);
}
return false;
}
public function __construct($returnUrl, $scopes, $clientSecret)
{
$this->client = $this->initClient($scopes, $clientSecret, $returnUrl);
$this->Login();
}
}
?>
How does one make sure that the application will not need to prompt users to authenticate, staying authenticated outside the functionality the user is supposed to access?
--> Edit:
After attempting to implement the suggested method, by DaImTo, the following code generates a Google_Service_Exception, stating that the client is unauthorized to retrieve access tokens with this method. I am not sure where that goes wrong.
class AccessDrive
{
private $client;
public function __construct( $scopes, $credentials, $email, $toImpersonate )
{
putenv( "GOOGLE_APPLICATION_CREDENTIALS=".$credentials);
$scopelist =[];
foreach ($scopes as $scope) {
array_push($scopelist, $this->getScope($scope));
}
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope($scopelist);
$client->setSubject($toImpersonate);
$this->client = $client;
}
--> Edit:
If you downvote, I would like to know why so I can improve my current and future questions.
You are currently authencating using Oauth2. Oauth2 allows you to request access from a user to access their google drive account. Assuming they grant you access you are given an access token which allows you to access their data for an hour. If you request offline access you will be given a refresh token which you can use to request a new access token when ever the access token expires.
In your case becouse you are trying to access your own drive account always i sugest you look into using a service account instead. Service accounts are pre approved you share a folder on your google drive account with it and it will have access to that drive account as long as you dont remove the permissions. I have a post about how service accounts work Google developer for beginners service account
Example:
// Load the Google API PHP Client Library.
require_once __DIR__ . '/vendor/autoload.php';
// Use the developers console and download your service account
// credentials in JSON format. Place the file in this directory or
// change the key file location if necessary.
putenv('GOOGLE_APPLICATION_CREDENTIALS='.__DIR__.'/service-account.json');
/**
* Gets the Google client refreshing auth if needed.
* Documentation: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
* Initializes a client object.
* #return A google client object.
*/
function getGoogleClient() {
return getServiceAccountClient();
}
/**
* Builds the Google client object.
* Documentation: https://developers.google.com/api-client-library/php/auth/service-accounts
* Scopes will need to be changed depending upon the API's being accessed.
* array(Google_Service_Analytics::ANALYTICS_READONLY, Google_Service_Analytics::ANALYTICS)
* List of Google Scopes: https://developers.google.com/identity/protocols/googlescopes
* #return A google client object.
*/
function getServiceAccountClient() {
try {
// Create and configure a new client object.
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope([YOUR SCOPES HERE]);
return $client;
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
}
code ripped form my sample project serviceaccount.php
Usage:
require_once __DIR__ . '/vendor/autoload.php';
session_start();
require_once __DIR__ . '/ServiceAccount.php';
$client = getGoogleClient();
$service = new Google_Service_Drive($client);
All your requests then get sent using $service
I am developing a web app that fetches and displays google analytics data for users that are not technical enough to do this themselves.
To do this, I:
1) have users log in with OAuth
2) store the access token
3) create a Google_Client and give it this access token
4) use this Google_Client to fetch the analytics data
This works no problem for the first user. However, it fails with an 'Access Denied' response for the second user. Following through the PHP code, I discovered that this is because the Google API Client caches the original access token (in the file system at /var/tmp/google-api-php-client), and uses this one instead of the fresh access token I have provided.
How do I prevent the Google API Client from caching the access token in the file system?
(Background information on the cache the Google_Client is using: when you provide an access token, it stores this with a key derived from the token scope. As the scope remains the same when the access token changes, the Google_Client does not create a new cache entry for each access token.)
For me:
$client = \Google_Client();
//...
$client->getCache()->clear();
$client->setAccessToken($access_token);
worked perfectly.
We implemented our own cache that just drops the data on the floor:
namespace AppBundle\Factory;
use Google\Auth\CacheInterface;
class NullGoogleCache implements CacheInterface
{
public function get($key, $expiration = false)
{
return false;
}
public function set($key, $value)
{
//do nothing
}
public function delete($key)
{
//do nothing
}
}
Google recommends to use "another caching library" like StashPHP on its Github page:
https://github.com/google/google-api-php-client#caching
In order to have multiple Google Analytics accounts logged in, you could set the Google_Client cache to Google_Cache_Null
Google_Client $client = new Google_Client();
....
$googleCache = new Google_Cache_Null();
$client->setCache($googleCache);
Don't forget to add (adjust according to your setup)
use Google_Client;
use Google_Cache_Null;
You can check the default google cache directory (ubuntu) with:
$ ls /temp/google-api-php-client/
Information:
Google_Client
Google_Cache_Null