I've written a script that successfully queries Google for an access token using the PHP SDK.
My code,
require_once('_/libs/google/apiclient/autoload.php');
$client_id = '<MY_ID>';
$client_secret = '<MY_SECRET>';
$redirect_uri = '<REDIRECT_URL>';
// Config my apps information
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->addScope("https://mail.google.com/");
// When google redirects with code swap for access token
if (isset($_GET['code'])) {
$client->authenticate($_GET['code']);
<------------- Try to get both tokens ----------------->
$_SESSION['access_token'] = $client->getAccessToken();
$_SESSION['refresh_token'] = $client->getRefreshToken();
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
}
Once I get the authentication code as shown above and query for the access token through getAccessToken(); it returns with the creation and expiry time.
However when I query for the refresh token through getRefreshToken(); it returns nothing when outputting it to the screen.
There was a similar question asked here
The problem seems to be letting Google know you need offline access however answers to that question aren't transferable to the PHP SDK.
Although I was using JavaScript, I just went through this exact same issue. I solved it by adding the redirect_uri parameter to the initial token request call.
Here is my SO post and my solution: Google API - Oauth 2.0 Auth token flow
To clarify the flow, to get a refresh token you actually have to make a request to get a 'code' from Google (thus my parameter 'response_type': 'code'), and then you make another call with that code to get an access token and refresh token.
Related
I am using to generate an access token and it's working successfully, as the access token is for a short time of 1 Hour, I want to get the Refresh Token of the user and store in DB so that I can get the access token anytime I need.
below is my code in two files.
file Oauth.php
<?php
require 'vendor/autoload.php';
// Refer to the PHP quickstart on how to setup the environment:
$client = new Google_Client();
$client->setAccessType('offline');
$client->setAuthConfigFile('client_secret.json'); //file downloaded earlier
$client->addScope("https://www.googleapis.com/auth/calendar");
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL)); //redirect user to Google
2nd File get_token.php
<?php
require 'vendor/autoload.php';
// Refer to the PHP quickstart on how to setup the environment:
$client->authenticate($_GET['code']);
$access_token = $client->getAccessToken();
$client->setAccessToken($access_token);
?>
Response I am getting like this https://www.xxxxxxxxxxxxxxxxxxx.com/google_calendar/get_token.php?code=4/0AY0e-g7ZauFJQPlzm1KsNpeuTF8S_5alcpjX8TA9LN0GVJd2cD0gAAiDPU56j2C9sVKIfg&scope=https://www.googleapis.com/auth/calendar
Thanks In advance
I did this with the following
<?php
require '../vendor/autoload.php';
$client = new Google_Client();
$client->setAuthConfig('client_secret_234rcontent.com.json');
$client->addScope('https://www.googleapis.com/auth/calendar');
$client->setRedirectUri('https://' . $_SERVER['HTTP_HOST'] .
'/google_calendar2/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->setPrompt('consent');
$client->setApprovalPrompt("consent");
$client->setIncludeGrantedScopes(true); // incremental auth
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
Redirect uri code
<?php
require 'vendor/autoload.php';
$client = new Google_Client();
$client->setAuthConfigFile('client_secret_2344056t4.apps.googleusercontent.com.json');
$client->setRedirectUri('https://' . $_SERVER['HTTP_HOST'] .
'/google_calendar2/oauth2callback.php');
$client->addScope('https://www.googleapis.com/auth/calendar');
$credentials=$client->authenticate($_GET['code']);
$access_token = $client->getAccessToken();
// $refresh_token = $credentials['refresh_token'];
print_r($credentials);
When you get the access token returned from Google, what is in the response body? A refresh token is supposed to be passed back within the initial response based on OAuth 2.0 standard, and you will need to store this token somewhere safe. When you need to refresh the access token, you will need to pass in this stored refresh token with the request (maybe within the query string as a parameter), and then Google will send you back the new access token. Read through Google Documentation thoroughly as this should be explained in details!
As you could see in the response I get from Google OAuth above, the second picture is the response you are supposed to get, repeat your process of requesting the access token, and see if you get the response like what I get in the picture, and you will notice the refresh_token in the response body.
I am working on an analytics API project and when the access token expires after an hour I get "code:401, Invalid Credentials" and have to unset the php session to be able to re-authenticate. I have read that I should receive a refresh token automatically when first authorized but I'm not seeing it anywhere. I have also tried to set the refresh token manually, having obtained it from the oauth2 playground. How can I make this work? Should the refresh token be stored in $_SESSION initially?
The code in question:
require_once __DIR__ . '/vendor/autoload.php';
$client = new Google_Client();
$client->setAuthConfig(__DIR__ . '/client_secrets.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setIncludeGrantedScopes(true);
$client->addScope(Google_Service_Analytics::ANALYTICS_READONLY);
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$analytics = new Google_Service_Analytics($client);
} else {
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
Access token in $_SESSION
[access_token] => blahblahblah
[expires_in] => 3600
[scope] => https://www.googleapis.com/auth/analytics.readonly
[token_type] => Bearer
[created] => 1537207944
So, after much digging and tinkering I finally got it to work. I kept seeing reference to
$client->setAccessType('offline');
and
$client->setApprovalPrompt('force');
being the solution to my problem, but they weren't working for me. It turns out that they need to be added to oauth2callback.php, NOT any other reporting or management files. This was deceiving due to the other files also manipulating the $client object. I'm finally receiving a refresh_token in $_SESSION['access_token'] and it seems to be handling refreshes on it's own now without any further action.
You will only get a Refresh Token on the first auth. You might need to deauthorise your app and try again. You can store the RT anywhere you like, provided it's secure. To use your RT to obtain an Access Token, see https://developers.google.com/identity/protocols/OAuth2WebServer#offline
Maybe I'm not understanding how the Youtube Data API is supposed to work. But if I have a PHP application that will generate the oauth request and receive the callback (which I have), the data it sends back is not a user's API key.
So I don't know if I am doing this wrong, or what, but their documentation is giving me a headache and I have searched and searched and I don't even know if my process is right here:
Web Server / OAuth Request (php)
require_once 'vendor/autoload.php';
$client = new Google_Client();
$client->setAuthConfig('oauth.json');
$client->setAccessType("offline"); // offline access
$client->setIncludeGrantedScopes(true); // incremental auth
$client->addScope(Google_Service_YouTube::YOUTUBE_READONLY);
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
Client Authorizes my Application
Google sends back (whatever this is)
'code' => string 'BLABLABLABLABLA' (length=89)
'scope' => string 'https://www.googleapis.com/auth/youtube.readonly https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile' (length=224)
Where is the user's API key, how do I get it? Is this not correct, or am I just jumbling stuff up here?
You don't get an API key when using OAuth. You get an access token. API key is obtained from a users's Google Developer Console.
Check the PHP Quickstart for a quick Youtube API OAuth reference in PHP.
your oauth2callback.php should look something like this. It will take the code and exchange it for an access token.
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/Oauth2Authentication.php';
// Start a session to persist credentials.
session_start();
// Handle authorization flow from the server.
if (! isset($_GET['code'])) {
$client = buildClient();
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$client = buildClient();
$client->authenticate($_GET['code']); // Exchange the authencation code for a refresh token and access token.
// Add access token and refresh token to seession.
$_SESSION['access_token'] = $client->getAccessToken();
$_SESSION['refresh_token'] = $client->getRefreshToken();
//Redirect back to main script
$redirect_uri = str_replace("oauth2callback.php",$_SESSION['mainScript'],$client->getRedirectUri());
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
Code ripped from my sample project on github
I'm trying to allow users to link their google calendar to my system, I've got it mostly working, and actually had it working for a while, but trying to make some modifications it's broken again :(
this page sends them to google:
require_once '../../resources/apis/google-api-php-client-2.2.1/vendor/autoload.php';
$client = new Google_Client();
$client->setAuthConfig('../../resources/apis/google-api-php-client-2.2.1/client_secret.json');
$client->setAccessType("offline"); // offline access
$client->setIncludeGrantedScopes(true); // incremental auth
$client->addScope(Google_Service_Calendar::CALENDAR);
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/sync/admin/apicomms/gcalmaster/syncFromGoogleCallback.php');
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
Then the callback goes to here:
require_once '../../resources/apis/google-api-php-client-2.2.1/vendor/autoload.php';
$client = new Google_Client();
$client->setAuthConfig('../../resources/apis/google-api-php-client-2.2.1/client_secret.json');
$client->setAccessType("offline"); // offline access
$client->setIncludeGrantedScopes(true); // incremental auth
$client->addScope(Google_Service_Calendar::CALENDAR);
//$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/sync/admin/broadcast/syncFromGoogleCallback.php');
$client->authenticate($_GET['code']);
$refresh_token = $client->getRefreshToken();
var_dump($_GET);
var_dump($refresh_token);
The callback page is showing
array(2) { ["code"]=> string(89) "x/xxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ["scope"]=> string(40) "https://www.googleapis.com/auth/calendar" } NULL
So it seems that the authentication is going smoothly, it's not throwing any errors...
What am I missing that is causing me to not get a refresh token? I've checked multiple times under https://myaccount.google.com/permissions to make sure my app isn't currently listed.
EDIT:
I have another script in my app which is currently working, it does the actual synchronisation, pulling the refresh token from the database, getting an access token with it, and succesfully pulling events from the calendar API. The thing that's not currently working is the 'adding' of new calendars to my app, the process of getting a refresh token from a new user
$refreshToken = $db->execute('pull refresh token from db');
require_once '../../resources/apis/google-api-php-client-2.2.1/vendor/autoload.php';
$client = new Google_Client();
$client->setAuthConfig('../../resources/apis/google-api-php-client-2.2.1/client_secret.json');
$client->fetchAccessTokenWithRefreshToken($refreshTokenFromDB);
$service = new Google_Service_Calendar($client);
$calendarId = 'primary';
$optParams = array(
'timeMin' => date('c'),
);
$results = $service->events->listEvents($calendarId, $optParams);
if (count($results->getItems()) == 0) {
print "No upcoming events found.\n";
} else {
//loop through stuff
}
You are close.
So it looks like you are successful in obtaining an authorization code.
Then with your authorization code you use the:
$client->authenticate($_GET['code']);
Keep in mind this is not a token. It is an authorization code.
You now have to request a access token using the authorization code.
$accessToken = $client->getAccessToken();
It's been awhile since I've tinkered around with this, but I think that this is also your only time that you will be able to request a refresh token.
So you will want to do this line next:
$refreshToken = $client->getRefreshToken();
You will want to securely save these tokens in a json file somewhere on your system to use the next time you want to do something with this calendar.
So when you do use this again, you will use the $accessToken to establish your credentials with google. If that access token is expired you will have to use the refresh token to get a new access token. I'm pretty sure the refresh token stays constant. If not it will be sent with the new access token. Then you re-save the json file. Rinse and repeat.
UPDATED
You will use:
$client->fetchAccessTokenWithRefreshToken($refresh_token);
To obtain a new access token.
I'm trying to make some script, that will automatically add an event from Joomla add new page to a Google calendar. I made it with this code and it even works, but only for a small amount of time.
I maybe misunderstood, how the Google authorization works. First i must authorize the script to use that calendar and so on. I made it once but after an hour i had to do it again. I would like to authorize it forever or at least for one year (something longer than one hour). And i can't even authorize it during the saving process, because its in saving script, that automatically redirect back to post management.
One more thing to mention: there is an redirect uri and i set it on index.php of administration pages. Not sure if its a problem, but it shouldn't because news are added at index.php?...... so it's still the same page.
But it all works for an hour (or something around that) so i believe that problem is not redirect uri.
THX
here is my code (its copy pasted from some google api example page):
require_once "google-api-php-client/src/Google_Client.php";
require_once "google-api-phpclient/src/contrib/Google_CalendarService.php";
session_start();
$client = new Google_Client();
$client->setApplicationName("Add Google Calendar Entries");
$client->setClientId('####');
$client->setClientSecret('####');
$client->setRedirectUri('####');
$client->setDeveloperKey('####');
$cal = new Google_CalendarService($client);
if (isset($_GET['logout'])) {
unset($_SESSION['token']);
}
if (isset($_GET['code'])) {
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF']);
}
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
if ($client->getAccessToken()) {
$calList = $cal->calendarList->listCalendarList();
$_SESSION['token'] = $client->getAccessToken();
$event = new Google_Event();
// some calendar adding stuff
} else {
$authUrl = $client->createAuthUrl();
print "<a class='login' href='$authUrl'>Authorize!</a>";
}
// Google Calendar API END
}
Just to summarize: I want my authorization to work longer and maybe there is a mistake in my code or in my understanding of how that works.
The key piece to get long term access if to request "offline" access from the user when you request authorization.
$client->setAccessType('offline');
$authUrl = $client->createAuthUrl();
This should return a refresh token as well as the current session token. You MUST save this refresh token. Even if they authorize the app again later, the refresh token will NOT be sent again. (And on that note, if they are already authorized and you have them authorize again after adding offline access, sometimes you won't get the refresh token.)
Then you can do a check like the following to keep them logged in:
$client->setAccessToken($site->token);
if($client->isAccessTokenExpired()) {
$NewAccessToken = json_decode($client->getAccessToken());
$client->refreshToken($NewAccessToken->refresh_token);
}
Finally, I should add that you will need to save the accessToken to the database instead of just the session to actually make it persist over different sessions. Hopefully that was obvious already.