I am using Google Drive API and authenticating using OAuth token using PHP program. For my test program I went to OAuth url manually and created a token. Then, I used it in the program and saved it to the file. From next time onwards the token is loaded from file and the program is able to access Google Drive. However, since the token would only be valid for 1 hr, after that I get the error since the token it retrieves from the file won't be valid anymore. I am not clear on how I can refresh the token so that its not required to manually obtain a new token.
Following is my code -
$client = new Google_Client();
$client->setClientId( CLIENT_ID );
$client->setClientSecret( CLIENT_SECRET );
$client->setRedirectUri( REDIRECT_URIS );
$client->setScopes($SCOPES);
$client->setAccessType('offline');
$client->setAuthConfigFile(CLIENT_SECRET_PATH);
If the cred file exist, I pull the token from that file else I would be fetching it. For now, the first time token I am setting manually -
if (file_exists(CREDENTIALS_PATH)) {
$accessToken = file_get_contents(CREDENTIALS_PATH);
} else {
$authCode = 'My Auth Code';
$accessToken = $client->authenticate($authCode);
}
file_put_contents(CREDENTIALS_PATH, $accessToken);
$client->setAccessToken($accessToken);
Now, if the token is expired I need to refresh the token automatically.
if ($client->isAccessTokenExpired()) {
//This is where I am running in trouble.
}
How do I achieve it? The target is to perform OAuth authentication and execute Google Drive API without any manual intervention.
Thank you for the help!
Looks like I found the answer.
I used the OAuth url for the first time access to include the parameter prompt=consent. So this generated the refresh-token that was saved in the file. After that, following addition to code did the trick -
if ($client->isAccessTokenExpired()) {
$client->refreshToken($client->getRefreshToken());
file_put_contents(CREDENTIALS_PATH, $client->getAccessToken());
}
After this the token is no longer expiring after an hour.
Related
I got a refresh token using OAuth playground, but I am not sure what to do with it. Using the php sample code to upload a video, I added this line of code
$client->refreshToken($refreshToken);
Is this all I need to do?
Few more steps are required once you have the OAuth2 refresh token:
Define the path to credentials file obtain from Google's dev console (also called "client-secret") and set it for $client:
$credentialsFilePath = "client_secret_file.json";
$client->setAuthConfig($credentialsFilePath);
Add the required scope (see here) which should match the scope defined when you received the refresh token (after first user's consent), this is a scope example for Gmail:
$client->addScope('https://mail.google.com/');
Set your refresh token (gets a new token and sets it to the $client):
$refreshToken = "1/Je...................";
$client->refreshToken($refreshToken);
Get your access token (which I like to store in session):
$_SESSION['access_token'] = $client->getAccessToken();
Start calling the API:
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
// API calls
}
Full code for Gmail requests using OAuth2 with a refresh token:
https://eval.in/776863
In my website i have an upload button for upload files to google drive via api.
Here is my code:
$auth_code = GOOGLEDRIVE_AUTH_CODE;
$access_token = GOOGLEDRIVE_ACCESS_TOKEN;
$refresh_token = GOOGLEDRIVE_REFRESH_TOKEN;
$client_id = 'Google_App_Client_ID';
$client_secret = 'Google_App_Client_Secret';
$redirect_uri = 'Redirct_Url';
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->addScope("https://www.googleapis.com/auth/drive");
$service = new Google_Service_Drive($client);
if (isset($access_token) && $access_token) {
$client->setAccessToken($access_token);
if ($client->isAccessTokenExpired()) {
$refresh_token = $client->getRefreshToken();
$client->refreshToken($refresh_token);
$access_token = $client->getAccessToken();
$co->save('GDRIVE_ACCESS_TOKEN',$access_token);
$co->save('GDRIVE_REFRESH_TOKEN',$refresh_token);
}
} else {
$authUrl = $client->createAuthUrl();
}
this line throws an error
$client->refreshToken($refresh_token);
"Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'"
Reading up on the error message it sounds like the token refresh isn’t working all of a sudden. Like I said, this upload tool has been working fine for months.
Any Idea ?
Thanks,
Midhun
Invalid_grant
Your server’s clock is not in sync with NTP. (Solution: check the server time if its incorrect fix it. )
If its not that then there is no fix besides asking the user to authenticate again. Possible causes for the refresh token to have expired.
The user has revoked your access.
the refresh token hasn't been used in six months to request a new access token.
The refresh token limit has been exceeded. Applications can request multiple refresh tokens. For example, this is useful in situations where a user wants to install an application on multiple machines. In this case, two refresh tokens are required, one for each installation. When the number of refresh tokens exceeds the limit, older tokens become invalid. If the application attempts to use an invalidated refresh token, an invalid_grant error response is returned. The limit for each unique pair of OAuth 2.0 client and is 25 refresh tokens (note that this limit is subject to change). If the application continues to request refresh tokens for the same Client/Account pair, once the 26th token is issued, the 1st refresh token that was previously issued will become invalid. The 27th requested refresh token would invalidate the 2nd previously issued token and so on.
So I've literally copied the code from the Google example (https://developers.google.com/google-apps/calendar/quickstart/php) and followed its instructions as best as I can, and then my calendar worked quite well. But I've come in today and realized that it stopped working, and I can't figure out what's wrong.
I believe the root of my problem is this line :
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
since that line gives me this error
Uncaught LogicException: refresh token must be passed in or set as part of setAccessToken
There are similar questions (Get refresh token google api, Not receiving Google OAuth refresh token) But I don't seem to be able to solve my problem with their answers.
Another note; $client->getRefreshToken() seems to be returning null when I test it, which is why I think $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken()); is failing.
So this is the piece of code directly from the example that is in question
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
That piece of code gives me errors because $client->getRefreshToken() is null, but I was under the assumption that I needed to use the refresh token to get a new token, which I can't do if there is no refresh token?
Also note, that this is already being set at the beginning of the calls
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
In your code
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
there is one important thing missing:
before you get your refresh token, you should check first if it exists, and if not - create it first. This is what it should look like:
// Refresh the access token if it's expired.
if ($client->isAccessTokenExpired())
{
// Refresh the refresh token if possible, else fetch a new one.
if ($client->getRefreshToken()) // checks if the refresh token exists
{
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken()); //creates an access token, provided that a refresh token exists
} else // If the refresh token does not exist, you have to create it first
{
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl); // you will be prompted to follow the authentification link and perform the authentification
print 'Enter verification code: '; // subsequently you will receive a verification code that you need to create a new token
$authCode = trim(fgets(STDIN));
Btw: If your refresh token already exists - it should be in the token.json file under your token path, however it could be invalid if you have changed the scopes after it was created. In this case, you should erase the token.json file, so that a new one can be generated.
Please note that access tokens have a limited lifetime and when it happens, this causes your Calendar to stop working. So, as suggested in Token expiration, you should write in your code to anticipate the possibility that a granted token might no longer work. This is where, refresh tokens becomes very useful for it allows your application to obtain new access tokens without going through the Authorization process again.
To request a refresh token, add access_type=offline to your authentication request. However, this GitHub post suggests that you could obtain the refresh token by providing the parameters
access_type=offline&approval_prompt=force in your request.
For more helpful tips, I think the following links will be beneficial:
The OAuth 2.0 Authorization Framework
OAuth2 and Google APIs Library for PHP
How and when to use the refresh token with PHP Google Calender API
I want to build a script that will check a Authenticated User's Google Calendar via the Google Calendar PHP Client. I was able to build a simple page that lets a user Auth and give permission to the Calendar Events. I receive a token and then grab a 15 upcoming events via:
$googleCal = new Google_Service_Calendar($googleClient);
$results = $googleCal->events->listEvents($calendarId, $optParams);
But what I'm struggling with is how to save this so I can have a script check this everyday to see if there were new events added. I think what I have is close just struggling to get over the finish line.
Thanks!
--
Update, I'm trying to use the refresh token, here is my code:
public function checkRedirectCode()
{
if(isset($_GET['code']))
{
$this->client->authenticate($_GET['code']);
// $this->setToken($this->client->getRefreshToken());
$this->setToken($this->client->getAccessToken());
$this->storeUser($this->getPayload());
return true;
}
return false;
}
public function setToken($token)
{
$_SESSION['access_token'] = $token;
$this->client->setAccessToken($token);
}
I have been able to echo the refresh token so I know I'm getting a proper refresh token but I'm getting errors whenever I use the commented out string. Any ideas?
To enable your script to be called beyond the lifetime of the original access token (which only last an hour), you will need to retrieve and store the refresh token during the initial authorisation, and then use this to generate a new access token each time your script runs.
Access tokens have limited lifetimes. If your application needs access
to a Google API beyond the lifetime of a single access token, it can
obtain a refresh token. A refresh token allows your application to
obtain new access tokens.
https://developers.google.com/identity/protocols/OAuth2#basicsteps (4. Refresh the access token, if necessary.)
After the user has authenticated your app, they are returned to your redirect URI with the code query-string.
The following is an example of authenticating and getting the refresh token (this uses Analytics, but should be the same for other services):
$client = new Google_Client();
$client->setClientId('xxx');
$client->setClientSecret('xxx');
$client->setRedirectUri('xxx');
//authenticate with the code returned from google
$authenticate = $client->authenticate($_GET['code']);
//get the refresh token
$refreshToken = $client->getRefreshToken();
//store refresh token in database...
Then, when you run your daily script, use the refresh token (retrieved from your database) to generate a new access token:
$client = new Google_Client();
$client->setClientId('xxx');
$client->setClientSecret('yyy');
$client->addScope(Google_Service_Analytics::ANALYTICS_READONLY);
$client->refreshToken($user->refresh_token);
$newToken = $client->getAccessToken();
$client->setAccessToken($newToken);
I am trying to get a refresh token for the Google API's, using the PHP SDK. I am authenticating the user with Javascript, retrieving a code, and exchanging it for an access_token server side, but this doesn't grant me an access token. What am I doing wrong? Here is the code I use:
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->addScope('https://www.googleapis.com/auth/plus.me');
$client->addScope('https://www.google.com/m8/feeds');
$client->setRedirectUri('postmessage');
$client->setAccessType('offline');
if (isset($_REQUEST['code'])) {
$client->authenticate($_REQUEST['code']);
if ($client->getAccessToken()) {
$_SESSION['access_token'] = $client->getAccessToken();
$token_data = $client->verifyIdToken()->getAttributes();
$result['data']=$token_data;
$result['access_token']=json_decode($_SESSION['access_token']);
}
}
debug($result); //my own function, var_dumps the content of an array
Here is the result of the array:
$result['access_token'] contains:
access_token: TOKEN
created: 1434380576
expires_in: 3594
id_token: IDTOKEN
token_type:"Bearer"
If I am not mistaken the first access token should also contain the refresh token, what am I doing wrong?
First check the settings in the developer console of Google to see if your RedirectUri is the same and that the API is activated (although if you already got that .json, then I assume it is.
You have to go through the Google Auth Prompt Screen at least 1 time to get a refresh token in your .json, and if your RedirectUri is taking you nowhere, you won't be able to get your refresh token or even the access validated.
You can also try a service account if you're doing small file transactions and don't need a user validation for the process of your script. Good Luck.
The problem was that I had to specify that I want offline access in the authentication process, the client side... The Google API's are horribly documented!!!