Google Drive API V3 with PHP client 2.0 - download file contents and metadata in a single request - google-api

I am trying to download files from Google Drive using PHP client v2.0 with Drive API V3.
Is it possible to retrieve file's body and metadata in a single HTTP request?
Supplying 'alt=media' to ->files->get() returns GuzzleHttp\Psr7\Response upon which I can run ->getBody()->__toString().
If I do not provide 'alt=media', then Google_Service_Drive_DriveFile is returned that has all the metadata, but does not have body.
Question.
Is it possible to get both metadata and body in the same request?

Try something like this:
<?php
// Look at this first: https://developers.google.com/api-client-library/php/auth/web-app
require_once __DIR__ . '/vendor/autoload.php';
//change to your timezone
date_default_timezone_set('Pacific/Auckland');
$client = new Google_Client();
$client->setAuthConfig('client_secrets.json');
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setIncludeGrantedScopes(true);
//change scopes to the ones you need
$client->addScope(Google_Service_Drive::DRIVE_FILE, Google_Service_Drive::DRIVE_APPDATA, Google_Service_Drive::DRIVE, Google_Service_Drive::DRIVE_METADATA);
$accessToken = json_decode(file_get_contents('credentials.json'), true);
$client->setAccessToken($accessToken);
//Refresh the token if it's expired. Google expires it after an hour so necessary
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents('credentials.json', json_encode($client->getAccessToken()));
}
$service = new Google_Service_Drive($client);
$results = $service->files->listFiles($optParams);
$fileId = 'yourfileid;
$file = $service->files->get($fileId, array('alt' => 'media'));
file_put_contents("hello.pdf",$file->getBody());
?>

Related

YouTube DATA API V3 does not return refresh token

I have almost looked through all the answers to this question and have not been able to solve my problem. I tried different options but can't get refresh token. Here's an example of my code.
$googleClient = new \Google_Client();
$redirectUrl = url('***');
$googleClient->setClientId(config('services.google_api_key.client_id'));
$googleClient->setClientSecret(config('services.google_api_key.client_secret'));
$googleClient->setScopes(config('services.google_api_key.scopes'));
$redirect = filter_var($redirectUrl);
$googleClient->setRedirectUri($redirect);
$googleClient->setAccessType('offline');
$googleClient->setApprovalPrompt('force');
if ($request->has('code')) {
$googleClient->authenticate($request->get('code'));
$token = $googleClient->getAccessToken();
dd($token);
}
And it returned only these data
[
"access_token" => "***"
"expires_in" => 3599
"scope" => "https://www.googleapis.com/auth/youtube"
"token_type" => "Bearer"
"created" => 1613189613
]
I know that the refresh token is provided only on the first call, and so I tried to use this method $googleClient->revokeToken($token); to get it again, but it didn't help, and I already spent a whole day trying to solve this problem and could not. Please help me solve this problem. I understand that I am making a mistake somewhere, but I cannot understand where. Thanks in advance.
I finally found the problem and would like to explain in detail what was the reason, maybe someone else will come in handy. And so I created everything as it was described in the official documentation https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps#obtainingaccesstokens.
I used this code and was unable to get the refresh token.
$googleClient = new \Google_Client();
$redirectUrl = url('***');
$googleClient->setClientId(config('services.google_api_key.client_id'));
$googleClient->setClientSecret(config('services.google_api_key.client_secret'));
$googleClient->setScopes(config('services.google_api_key.scopes'));
$redirect = filter_var($redirectUrl);
$googleClient->setRedirectUri($redirect);
$googleClient->setAccessType('offline');
$googleClient->setApprovalPrompt('force');
if ($request->has('code')) {
$googleClient->authenticate($request->get('code'));
$token = $googleClient->getAccessToken();
dd($token);
}
The official documentation says this:
$client = new Google_Client();
$client->setAuthConfig('client_secret.json');
$client->addScope(GOOGLE_SERVICE_YOUTUBE::YOUTUBE_FORCE_SSL);
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');
// offline access will give you both an access and refresh token so that
// your app can refresh the access token without user interaction.
$client->setAccessType('offline');
// Using "consent" ensures that your application always receives a refresh token.
// If you are not using offline access, you can omit this.
$client->setApprovalPrompt("consent");
$client->setIncludeGrantedScopes(true); // incremental auth
Pay attention to this part:
// Using "consent" ensures that your application always receives a refresh token.
// If you are not using offline access, you can omit this.
$client->setApprovalPrompt("consent");
And this is what I managed to find out
$client->setApprovalPrompt('force'); // works
$client->setApprovalPrompt('consent'); // doesn't work
$client->setPrompt('consent'); // works
For 'consent' setApprovalPrompt() doesn't work, but I could not find about it in the official documentation. And found this error from here https://github.com/googleapis/google-api-php-client/issues/1795.
My final code looks like this which works fine and returns me a refresh token:
$redirectUrl = url('my_callback_url');
$client = new \Google_Client();
$client->setClientId(config('services.google_api_key.client_id'));
$client->setClientSecret(config('services.google_api_key.client_secret'));
$client->addScope(config('services.google_api_key.scopes'));
$client->setState(substr(md5(time()), 0, 15));
$client->setRedirectUri($redirectUrl);
$client->setAccessType('offline');
$client->setPrompt('consent');
$client->setIncludeGrantedScopes(true);
return $client->createAuthUrl();
Thank you stvar for your answer, after that I started to read the documentation all over again and look for the reason.
adding consent prompt fixed the issue. for js library it will look like this:
const authorizationUrl = oauth2Client.generateAuthUrl({
// 'offline' will get refresh_token
access_type: 'offline',
scope: scopes,
prompt: 'consent',
state: stateName,
});

Google MyBusiness access from webservers without redirects

As detailed in my question to the the Google API team, I would like to work out a way to avoid redirects.
In theory this should be possible as an Authentication Code from one client (JavaScript) should be agnostic of the client and thus it should work if passed to the PHP client to fetch the access and refresh tokens.
Steps in theory:
Client gets an authorization code
Client exchanges the authorization code for the access and refresh tokens
How am I attempting this?
Run the JavaScript client to get the Authentication token
GoogleAuth = gapi.auth2.getAuthInstance()
GoogleAuth.grantOfflineAccess({
scope: 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/plus.business.manage https://www.googleapis.com/auth/plus.me openid email profile'
}).then(function (resp) {
var auth_code = resp.code;
console.log("AuthCode:" + auth_code)
})
At this point i get a code, not sure if this is an authorization code or access token but i cannot see any other function in the Javascript library that is Authorization token explicit/specific.
Use the Authentication Token in the PHP API Library
$error = $request->get('error');
$code = $request->get('code');
if($error){
throw new Exception('Error from authenticating ' . $error);
}
$client = new \Google_Client();
$client->setAuthConfig(getcwd() . '/../client_secret.apps.googleusercontent.com.json');
$client->setAccessType("offline"); // offline access
$client->setIncludeGrantedScopes(true); // incremental auth
$client->addScope(
array(
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/plus.business.manage'
)
);
$client->setRedirectUri('http://myserver.com/code');
$client->setApprovalPrompt('force');
$client->fetchAccessTokenWithAuthCode($code);
$accessToken = $client->getAccessToken();
return new Response(
"<html><body>Authenticated with code : " . $code . "<br/>\n\n".
" Access Token is : ". var_export($accessToken, true) . "</body></html>"
);
Access token is still null. the line $accessToken = $client->getAccessToken(); returns null or false.
This works in the full PHP based version but the PHP version is based on creating a link that the user needs to follow, the user is then on the Google server and can approve the app, when approved the user is redirected back to the app. Then the app receives the Authentication code.
I just would like to avoid redirects due to the architecture of single pages apps or just preference. The only alternative I can think of is to open a popup and notify the original window when access and refresh codes are returned so that the PHP client can go on querying the API, but it is an ugly solution IMHO.
Is there another way to get an authorization code that works on PHP but obtained from JavaScript?

How to retrieve respons status from Street View Image Metadata using guzzle

I am using guzzle to call to Street View Image API from my laravel application. I want to be able to retrieve the status code from my request as explained in the end of the docs.
I specifically want to catch the:
{
"status" : "ZERO_RESULTS"
}
here is my Guzzle code from my controller (am including guzzle correctly in namespace). The address in the get call is generating a "Sorry we have no imagery here":
$client = new Client();
$res = $client->get('https://maps.googleapis.com/maps/api/streetview?size=800x600&location=78.648401,14.194336&key=my-API-key&fov=120&heading=90');
$res->getStatusCode();
dd($res->getBody());
BUT. As u can see in the pic the meta-data is empty. When I dd the $res->getStatusCode(); it gives me a 200.
How do I catch the ZERO_RESULT ?
Sir, please note that there are two different endpoints; one for metadata and one for image retrieval. You only used the image retrieval. Here is an example method checking if a given coordinate has an image
private function coordinateHasImage($coordinate)
{
$client = new Client();
$res = $client->get( 'https://maps.googleapis.com/maps/api/streetview/metadata?size=600x300&location=' . $coordinate . '&fov=90&heading=235&pitch=10&key=YOUR_KEY_HERE');
return json_decode($res->getBody()->getContents())->status == "OK";
}

Dynamic callback url laravel

I tried to make my callback url dynamic because I'm configuring socialite in a multi auth system. I tried to use the socialiteproviders/manager as below:
$clientId = env($provider."_client_id");
$clientSecret = env($provider."_client_secret");
$redirectUrl = "the url i want";
$config = new \SocialiteProviders\Manager\Config($clientId,$clientSecret,$redirectUrl);
return Socialite::with($provider)->setConfig($config)->redirect();
but it says:
Call to undefined method Laravel\Socialite\Two\FacebookProvider::setConfig()
when trying to login with facebook.
Can someone please help me? Thank you.
I could reproduce and found a solution. The code you provided was outdated, and I found other instances of it here: https://laravel.io/forum/07-28-2016-dynamic-callback-url-laravel-socialite
By default, Socialite will get the provider config in services.php by passing the $providerName = facebook
So your code now becomes:
// The services.php config will return null, fix it by using: strtoupper()
$clientId = env(strtoupper($provider . "_client_id"));
$clientSecret = env(strtoupper($provider . "_client_secret"));
$redirectUrl = "/the-url-i-want";
// ->redirect() acts as a closure, without it, you'll get an error like:
// "Serialization of 'Closure' is not allowed"
$user = Socialite::with($provider)->redirect();
return redirect()->to($redirectUrl)->with(['user', $user]);
More info on redirecting with session data:
https://laravel.com/docs/6.x/redirects#redirecting-with-flashed-session-data

Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'

I am working with this package Analytics-Laravel 4 for google analytics and I have follower all of the steps correctly. When I try to get the site id for example, I face this error:
Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'
I have double checked all of the configurations, client id, service_account and private key but the error still occurs.
Anything else I should try to check that maybe would solve this issue?!
I didn't use this package before, I'm using google-api-php-client, but anyways, this error occurs if you don't set the refresh token.
You should know that you need to have the access token ONLY once. You also need to set the access type to be offline, which will provide you with a refresh token that you will use to automatically get a new access token without getting a new code every time your access token expires.
In google's console, I created a Client ID for web application. Make sure you set the redirect URI to your web page where you will receive the code and will extract the access token using that code.
Here is a code example using google-api-php-client, I hope it will help:
You need to run the following code only once, and retrieve and store the access token.
<?php
require_once('google-api-php-client-master/src/Google/Client.php');
session_start();
$client = new Google_Client();
$client->setApplicationName('APP_NAME');
$client->setClientId(YOUR_CLIENT_ID);
$client->setClientSecret('YOUR_CLIENT_SECRET');
$client->setRedirectUri('YOUR_REDIRECT_URI');
$client->setDeveloperKey('YOUR_DEV_KEY');
$client->setScopes(array('https://www.googleapis.com/auth/analytics.readonly'));
$client->setAccessType("offline");
// Step 1: Create an auth url
if (isset($_GET['ref']) && $_GET['ref'] == "1") {
$authUrl = $client->createAuthUrl();
return Redirect::to($authUrl);
}
// Step 2: The user accepted your access now you need to exchange it.
if (isset($_GET['code'])) {
$client->authenticate($_SESSION['code']); //Authenticate the client
$token = $client->getAccessToken(); //Get the access token
var_dump($token); //Store the token in your DB or config file
die();
}
?>
After getting your access token from the code above (which should contain a refresh token), store it in your DB or a config file.
Now the following code should authenticate the client and refresh the access token when it expires via the getAccessToken function
<?php
require_once('google-api-php-client-master/src/Google/Client.php');
require_once('google-api-php-client-master/src/Google/Service/Analytics.php');
$client = new Google_Client();
$client->setApplicationName('APP_NAME');
$client->setClientId(YOUR_CLIENT_ID);
$client->setClientSecret('YOUR_CLIENT_SECRET');
$client->setRedirectUri('YOUR_REDIRECT_URI');
$client->setDeveloperKey('YOUR_DEV_KEY');
$client->setScopes(array('https://www.googleapis.com/auth/analytics.readonly'));
$client->setAccessType("offline"); //Make sure the access type is offline to get a refresh token
$config = CoreConfig::find(1); //Getting the first record from the config table
$client->setAccessToken($config->google_access_token); //Retrieve the access token that you stored and set it to the client object
//Check this the token is expired
if($client->isAccessTokenExpired()) {
$token = json_decode($config->google_access_token, true); //Get the token stored, and convert JSON to array
$client->refreshToken($token['refresh_token']); //Set the refresh token
$newtoken = $client->getAccessToken(); //Call the getAccessToken() function to get a new access token for you
$config->update(array('google_access_token' => $newtoken)); //Store the new token in your DB
}
if ($client->getAccessToken()) {
$analytics = new Google_Service_Analytics($client);
//Do something with the $analytics object
}
?>
It could be the server time. If the local time on your server is out of sync with google's oAuth server even by a few seconds you'll get that error message.
You can check the time by running "date" in the console.
Running "sudo ntpdate ntp.ubuntu.com" solved it for us.

Resources