Google PHP API not returning refresh token - php

I am working on a web application that, among other things, reads the gmail of a signed in user. I am using the Google PHP library to handle authentication, and I am able to fetch emails without problems. The problem is that the access token expires after 1 hour. The documentation indicates that you should get a refresh token with the initial authorization, but when I call $client->getRefreshToken(), it returns null.
I understand that the refresh token is only returned the first time that a user signs in. While testing this, I revoked the authorization on the account, and then tried again, but it still does not give me a refresh token. What am I doing wrong?
Edit: I had a friend sign in with his personal Google account, which has never been used on the site, and still, no refresh token was given.
Source code:
Login.php: The first page
<?php
session_start();
require_once 'vendor/autoload.php';
$force=(isset($_GET['force'])&&$_GET['force']='true');
if(!isset($_SESSION['google_token'])||force)
{
$client=new Google_Client();
$client->setAuthConfig('/path/to/client_secret.json');
$client->addScope('https://www.googleapis.com/auth/gmail.readonly');
$client->addScope('https://www.googleapis.com/auth/userinfo.email');
$client->addScope('https://www.googleapis.com/auth/userinfo.profile');
$client->setState('offline'); //I realized that the mistake is here, see my answer below.
$client->setApprovalPrompt('force');
if($force)
$client->setPrompt('consent');
$client->setRedirectUri('https://example.com/doLogin.php');
$auth_url=$client->createAuthUrl();
header("Location: " . filter_var($auth_url, FILTER_SANITIZE_URL));
die();
}
header('location: /index.php');
die();
doLogin.php: The Google Oauth page redirects here.
<?php
session_start();
require_once 'vendor/autoload.php';
if(isset($_GET['error']))
{
session_unset();
header('location: /error.php?error='.urlencode('You must be signed into Google to use this program.'));
die();
}
$client=new Google_Client();
$client->setAuthConfig('/path/to/client_secret.json');
$client->setRedirectUri('https://example.com/doLogin.php');
$client->setAccessType('offline');
$client->authenticate($_GET['code']);
$access_token=$client->getAccessToken();
$refresh_token=$client->getRefreshToken();
$service=new Google_Service_Oauth2($client);
$email=$service->userinfo->get()['email'];
$_SESSION['email']=strtolower($email);
$_SESSION['access_token']=$access_token;
$_SESSION['refresh_token']=$refresh_token;
header('Location: /index.php');
die();
?>

OK, so after looking around, and poking through the raw http, I found my mistake. I was calling $client->setState('offline'); instead of $client->setAccessType('offline'); I fixed that, and now I am receiving the refresh token when I should.

Related

how to get Refresh token in google oauth 2.0 api with PHP

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.

Why cant I get Data from the Google OAuth API through PHP?

I have a login script created which allows you to login through the Google API. Everything works fine and the redirect back to my website does also work.
Here is my script for the oauth callback:
<?php
require_once 'google-api-php-client-2.2.1/vendor/autoload.php';
session_start();
if (isset ($_GET["error"])) {
header('Location: #');
}
if (isset ($_GET["code"])) {
$client = new Google_Client();
$client->authenticate($_GET['code']);
$_SESSION['g_access_token'] = $client->getAccessToken();
$redirect = "http://".$_SERVER["HTTP_HOST"].$_SERVER["PHP_SELF"];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
}
if (isset ($_SESSION["g_access_token"])) {
$plus = new Google_Service_Plus($client);
$client->setAccesToken($_SESSION["g_access_token"]);
$me = $plus->people->get('me');
$_SESSION["unid"] = $me["id"];
$_SESSION["g_name"] = $me["displayName"];
$_SESSION["email"] = $me["emails"][0]["value"];
$_SESSION["g_profile_image_url"] = $me["image"]["url"];
$_SESSION["g_cover_image_url"] = $me["cover"]["coverPhoto"]["url"];
$_SESSION["g_profile_url"] = $me["url"];
}
?>
I dont get any error, but the session variables are still undefined. Where is the problem?
I just read and watched lots of tutorials, but still dont get it. Maybe somebody can help me here!
Thank you :)
EDIT
I just debugged my code and now I know that the Session Variable 'g_access_token' is empty after the login. So the main reason why it doesnt work should be the authentication. But I still dont know how to solve that...
I've just come from dealing successfully with Google OAuth API for PHP. So far, your code doesn't seem broke. Anyhow, it'd be nice to check your config file from which you get the class new Google_Client().
As that file is not posted in your question, i recommend you to follow this one, and call it through require_once to whenever you need it:
<?php
session_start();
require_once "GoogleAPI/vendor/autoload.php";
$gClient = new Google_Client();
$gClient->setClientId("your_client_id_comes_here");
$gClient->setClientSecret("your_client_secret");
$gClient->setApplicationName("CPI Login Tutorial");
$gClient->setRedirectUri("your_authorized_uri.php");
//addScope sets the permissions you'll have in your app, whether it is only for displaying them or to handle as you desire (e.g.: store account's information, such as email givenName and so on, into your own db
$gClient->addScope("https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/plus.me");
?>
Also, don't you forget to enable the Google + API in your Google console. Finally, check that in your credentials, the "Authorized redirect URIs" contains the redirect uri you want the google api to callback once the user is authenticated.

OAuth2 callback: redirect after getting an access token by authenticating a code

Taking a look at this google client api for php example
I noticed this part of the code:
/************************************************
If we have a code back from the OAuth 2.0 flow,
we need to exchange that with the authenticate()
function. We store the resultant access token
bundle in the session, and redirect to ourself.
************************************************/
if (isset($_GET['code'])) {
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
}
The thing is I don't get the redirect (header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));). In fact you can remove it and everything seems to go on just fine. So, why is it there?
Which redirect you don't get?
The code-for-token-exchange is part of the standard Oauth2 flow (the so called "Authorization Grant"). You can read about the exchange here: https://docs.auth0.com/protocols#4 (or in the spec here: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-31#section-4.1). The spec is a little bit harder to parse.
The code is posted to a specific location on your website (/callback), because it is usually a fixed location registered with the IdP. Any other redirects after that, are often to send the user to the actual resource they wanted to land on originally. Or simply to the (authenticated) home. For example: this might happen if the user wants to go to https://yoursite/someplace but they need to be authenticated: the entire flow happens, and afterwards they are redirected to /someplace.

Google api authorization for long time

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.

Unauthorized 401 on Google+ API moments.insert with PHP

I want to create a small bot, which can push G+ moments to G+ page under certain conditions. Want to try it with PHP. So I have: registered web app in Google Console, Google+ API turned on, small PHP-script on my Apache server and 401 Unathorized in result. I'm using google-api-php-client from https://github.com/google/google-api-php-client
I've searched answer through the Internet, but nothing've found. Everywhere everyone have had their own happy end and all worked, but I've try all of it - and only 401 came to me.
My script:
<?
header("Content-Type: text/html; charset=utf-8");
session_start();
ini_set("display_errors", 1);
require_once 'Google/Client.php';
require_once 'Google/Service/Plus.php';
$client = new Google_Client();
$client->setClientId("MY_CLIENT_ID");
$client->setClientSecret("MY_CLIENT_SECRET");
$client->setRedirectUri("REDIRECT_URL");
$client->setAccessType('offline');
$client->addScope("https://www.googleapis.com/auth/plus.login");
$client->addScope("https://www.googleapis.com/auth/plus.me");
$client->addScope("https://www.googleapis.com/auth/userinfo.profile");
$client->addScope("https://www.googleapis.com/auth/userinfo.email");
$requestVisibleActions = array('http://schemas.google.com/AddActivity');
$client->setRequestVisibleActions($requestVisibleActions);
if (!isset($_GET['code'])) {
if (isset($_SESSION['access_token'])) {
$client->setAccessToken($_SESSION['access_token']);
$moment_body = new Google_Service_Plus_Moment();
$plus = new Google_Service_Plus($client);
$moment_body->setType("http://schemas.google.com/AddActivity");
$item_scope = new Google_Service_Plus_ItemScope();
$item_scope->setId("target-id-214wdefsadf1");
$item_scope->setType("http://schemas.google.com/AddActivity");
$item_scope->setName("The Google+ Platform");
$item_scope->setDescription("A page that describes just how awesome Google+ is!");
$item_scope->setImage("https://developers.google.com/+/plugins/snippet/examples/thing.png");
$moment_body->setTarget($item_scope);
$momentResult = $plus->moments->insert('me', 'vault', $moment_body);
} else {
$authUrl = $client->createAuthUrl();
header("Location: ".$authUrl);
exit;
}
} else {
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
header("Location: REDIRECT_URL");
exit;
}
So script succefully requests all needed access accordingly to scopes registered in client, gets a token, writes it into session, but when it try to insert new moment it gets Fatal error: Uncaught exception 'Google_Service_Exception' with message 'Error calling POST https://www.googleapis.com/plus/v1/people/me/moments/vault: (401) Unauthorized' in /var/www/rico/Google/Http/REST.php:79
What is wrong? And I've try some solutions placed here, on stackoveflow, but they didn't help. I also checked my authURL - it seems OK, with correct request_visible_actions and other... I don't know what is wrong...
You may be missing a few things. Maybe your example assumes you are putting the real values for some of these you have set. However you will want ApplicationName and DeveloperKey set. Many api calls will return not authorized if you don't have the developer key set for sure.
$client->setApplicationName($plApp->authenticationData->applicationName);
$client->setDeveloperKey($authData->apiKey);
I presume you are actually setting these below. Return url must match that which was set for the application on the google developer site for the specific application
$client->setClientId($authData->clientId);
$client->setClientSecret($authData->clientSecret);
$client->setRedirectUri($returnUrl);

Categories