How can I reach users' calendar information on server? - php

Users authorize in my android application. And I am sending users' token and other information to my server. At this server I want to implement some logic for users.
I want to have exactly this flow.
I followed the steps quickstart.php in this link to get users' calendars on server.
But I get following error :
google oauth exception' with message 'could not json decode the token'
For this reason I tried this solution.
But i take same error. So as 3rd option I created the json format myself like in this solution as below.
$access_token = '{
"access_token":'.$access_token.',
"token_type":"Bearer",
"expires_in":3600,
"id_token":'.$id_token.',
"refresh_token":" ",
"created":'. time() .'
}';
as you see I do not know how to exchange refresh token . I searched how to get refresh token and saw this question. And implemented this solution to my code but nothing changed.
Edit 4 : I tried to get access token according to this answer at android application and send it to app server. I'm taking the code as before I did :
GoogleSignInAccount acct = result.getSignInAccount();
code = acct.getServerAuthCode();//sending this code to AsyncTask to get access token
my function to get Access Token:
private void getAccessToken()throws GoogleAuthException, IOException{
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
List<String> scopes = new LinkedList<String>();
scopes.add("https://www.googleapis.com/auth/calendar");
scopes.add("https://www.googleapis.com/auth/calendar.readonly");
scopes.add("https://www.googleapis.com/auth/urlshortener");
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(transport, jsonFactory, client_id, client_secret, scopes).build();
try{
GoogleTokenResponse res = flow.newTokenRequest(code).execute();
accessToken = res.getAccessToken();
}catch(IOException e){
}
at the php server side I changed user-example.php file little bit as below because I have the access token now:
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setAccessType("offline");
$client->setRedirectUri($redirect_uri);
$client->addScope("https://www.googleapis.com/auth/urlshortener");
$service = new Google_Service_Urlshortener($client);
if (isset($_REQUEST['logout'])) {
unset($_SESSION['access_token']);
}
$client->setAccessToken('{"access_token":"'. $access_token .'","token_type":"Bearer","expires_in":3600,"created":'. time() .'}');
if ($client->getAccessToken() && isset($_GET['url'])) {
$url = new Google_Service_Urlshortener_Url();
$url->longUrl = $_GET['url'];
$short = $service->url->insert($url);
$_SESSION['access_token'] = $client->getAccessToken();
But now I'm getting below error:
Fatal error: Uncaught exception 'Google_Service_Exception' with message 'Error calling POST https://www.googleapis.com/urlshortener/v1/url: (403) Insufficient Permission' in C:\wamp\www\google-php\src\Google\Http\REST.php on line 110

I was getting Insufficient Permission error after I started to use GoogleAuthorizationCodeFlow to get access token as I mentioned in my OP. And then I tried to add Calendar scope to GoogleApiClient.Builder(this) but I get error like I can't add scope if I add Auth.GOOGLE_SIGN_IN_API because I had added Auth.GOOGLE_SIGN_IN_API to GoogleApiClient.Builder. So this time I tried to add the scope to GoogleSignInOptions.Builder and it is working now. I'm able to get both refresh token and access token. Below code solved my problem:
GoogleSignInOptions gso = state.getGso();
if(gso == null){
gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(new Scope("https://www.googleapis.com/auth/calendar"))
.requestIdToken(getString(R.string.server_client_id))
.requestEmail()
.requestServerAuthCode(getString(R.string.server_client_id), false)
.requestProfile()
.build();
}

Related

Get Google Analytics API token / Google_Client getAccessToken returns null / empty

I'm trying to get a Google access token for a service account so I can access the Analytics API from the client side.
On the server side I have the following code:
$keyFile = base_path() . '/keys/xxxxxxxx-xxxxxxxxx.json';
$client = new Google_Client();
$client->setApplicationName('Analytics Demo');
$client->setAuthConfig($keyFile);
$client->setScopes(['https://www.googleapis.com/auth/analytics.readonly']);
$token = $client->getAccessToken(); // Returns null
At some point it returned a token, but after a couple of calls it started returning null.
Trying to get the $client->getRefreshToken() also returns null.
When I put $client in a Google_Service_Analytics object everything seems to work just fine, so I know the key file is correct and properly read:
$analytics = new Google_Service_Analytics($client);
$accounts = $analytics->management_accounts->listManagementAccounts(); // Returns proper data
Any ideas on what might be going wrong?
UPDATE:
Calling $client->isAccessTokenExpired() results in true so I guess I have to refresh the token, but I have no clue how.
After a lot of trial and error I found that I had to use $client->fetchAccessTokenWithAssertion() instead of $client->getAccessToken().
public static function getGoogleServiceToken():?string {
$keyFile = base_path() . '/keys/xxxxxxxx-xxxxxxx.json';
$client = new Google_Client();
$client->setAuthConfig($keyFile);
$client->setApplicationName('Analytics Demo');
$client->setAccessType('access_type');
$client->setScopes(['https://www.googleapis.com/auth/analytics.readonly']);
if ($client->isAccessTokenExpired()) {
$token = $client->fetchAccessTokenWithAssertion();
}
else {
$token = $client->getAccessToken();
}
return $token['access_token'];
}
Client side:
...
private _handleGetResponse(response) {
...
gapi.analytics.auth.authorize({
serverAuth: {
access_token: response.token,
}
});
...
Not sure why there is no decent documentation on this at all. Even knowing that I have to use this method I can't find documentation on what this method is actually doing. Any explanation or links to some documentation are still welcome!

Google Events API returning 401 error unauthorized_client

I have created an application in laravel to sync my users calendar in my database with incremental sync every thing working perfect when i am running it in my browser.
but its returning 401 unauthorized_client in my cron 100% same code implemented in both files.
$client = $this->getGoogleClient();
$client->setAccessToken($accessToken);
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
}
$gCalService = new Google_Service_Calendar($client);
$optArr = array();
$calendarId = 'primary';
$pageToken = NULL;
$responceEvents = array();
$synced = $emailSync->getGoogleSynced();
if($synced != null)
$optArr['syncToken'] = $synced->sync_token;
do {
if ($pageToken) {
$optArr['pageToken'] = $pageToken;
}
try {
$results = $gCalService->events->listEvents($calendarId,$optArr);
}
catch (Google_Service_Exception $e) {
$msg = $e->getMessage();
return $msg;
}
if ($results->getItems()) {
$responceEvents = array_merge($responceEvents, $results->getItems());
$pageToken = $results->getNextPageToken();
}
} while ($pageToken);
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
returning below error
Array
(
[error] => unauthorized_client
[error_description] => Unauthorized
)
debugged code line by line $client->getRefreshToken() also contain value which i am passing here $client->setAccessToken($accessToken); exactly same code working fine in my browser file and not working in my cron.
401 unauthorized_client
Means that your code has not been authorized. If you want to access private user data you will need to be logged in. Your code needs an access token in order to access the Google api.
What you should be doing is authorizing your code once using Oauth2 in a browser save the refresh token someplace and have the cron job read the refresh token and request a new access token before it runs.
Assuming that you own the account you are trying to insert into you may also want to consider using a service account.
Refresh token
If you are storing the refresh token in the database you must assign it to your client first before running your code here i am doing that with a refresh token stored in a session variable.
$client->refreshToken($_SESSION['refresh_token']);

Wrong number of segments in token (OAuth Google Api with php)

I have a php application that use OAuth2 to authenticate users to their account.
until yesterday, everything worked very well.
But today, and without changing my code, when I try to access to my account, and after I authenticates to my google account, I obtain a blank page.
I debug the code and I found that it crashed when the Google_Client try to verifyIdToken and more exactly in the function verifySignedJwtWithCerts because : $segments = explode(".", $jwt); find 4 segments and not 3.
here is my code :
...
$client = new Google_Client();
$client->setClientId($clientId);
$client->setClientSecret($clientSecret);
$client->setRedirectUri($redirectUri);
$client->setScopes("email");
if(!isset($_GET['code']))
header("Location: ".$client->createAuthUrl());
else
{
$client->authenticate($_GET['code']);
$_SESSION["access_token"] = $client->getAccessToken();
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
} else {
$authUrl = $client->createAuthUrl();
}
$ticket = $client->verifyIdToken();
if ($ticket) {
$admin = $ticket->getAttributes();
}
$_SESSION["email"] = $admin["payload"]["email"];
...
Can anyone offer an explanation?
Thank's in advance
Some how I didn't find any answer in Google. So I going to share what I do to make it work.
The short answer: Because the token you use in verifyIdToken() is invalid.
The long answer as below:
I'm not sure if it is the correct steps:
After Google Login Authentication, we will get the "Authorization
Code" from oauthplayground (for testing). Please enter your oauth client_id and oauth client secret in the setting before usage.
I have use the "Authorization Code" to get the other tokens by writing
$token = $client->fetchAccessTokenWithAuthCode("THE_AUTHORIZATION_CODE");
Inside $token, I have receive the following: access_token, token_type, expires_in, refresh_token, id_token, created.
Now use the id_token in $payload = $client->verifyIdToken(id_token);
Then you will get the correct information you needed like names, aud, exp, iss etc in $payload.
Note: To avoid your code return the "Wrong number of segments in token" error again, you have to try and catch the verifyIdToken method
try {
$payload = $client->verifyIdToken(id_token);
}
catch (Exception $e) {
echo "Invalid id token";
}
On my side, I've been working on MERN Application and, the issue was on the way I send the tokenId on the backend.
Check if the idToken you are sending to the backend is the same as the one you are getting on googleSuccess.
before.
const onGoogleSuccess = (response) => {
const tokenId = response.accessToken;//Wrong
console.log("SUCCESS::", response);
dispatch(login({ tokenId })).then((res) => {
console.log("GOOGLE-LOGIN::", res);
});
};
After:
before.
const onGoogleSuccess = (response) => {
const tokenId = response.accessToken;
console.log("SUCCESS::", response);
dispatch(login({ tokenId })).then((res) => {
console.log("GOOGLE-LOGIN::", res);
});
};

Unable to refresh OAuth2 token in PHP, invalid grant

I need to make a PHP script that creates a sigle event on Google Calendar.
I had no problems setting up client id, client secret, dev key and creating a new event.
My only problem is with OAuth2, in particular I need to make a permanent connection and I do not want to do the authentication everytime I run the script.
Actually, with this script I'm able to get a token and a refresh token, but every hour my token expires and I don't know how to refresh it. How can I edit this code to do that?
Can I save both the token and the refresh token somewhere and always use the same data?
I obtain an uncaught exception 'Google_AuthException' with message 'Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant"
I have already read some other posts about this topic here in stackoverflow but I still haven't found a solution... :\
<?php
require_once 'src/Google_Client.php';
require_once 'src/contrib/Google_CalendarService.php';
session_start();
$client = new Google_Client();
$client->setApplicationName("Calendar App");
$client->setClientId('xxxx');
$client->setClientSecret('yyy');
$client->setRedirectUri('http://www.zzzzzz');
$client->setDeveloperKey('kkk');
$client->setAccessType('offline');
$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'])) {
//echo $_SESSION['token'];
//$client->setAccessToken($_SESSION['token']);
$authObj = json_decode($_SESSION['token']);
$accessToken = $authObj->access_token;
$refreshToken = $authObj->refresh_token;
$tokenType = $authObj->token_type;
$expiresIn = $authObj->expires_in;
echo 'access_token = ' . $accessToken;
echo '<br />';
echo 'refresh_token = ' . $refreshToken;
echo '<br />';
echo 'token_type = ' . $tokenType;
echo '<br />';
echo 'expires_in = ' . $expiresIn;
}
if(!empty($cookie)){
$client->refreshToken($this->Cookie->read('token'));
}
if ($client->getAccessToken()) {
$calList = $cal->calendarList->listCalendarList();
$_SESSION['token'] = $client->getAccessToken();
} else {
$authUrl = $client->createAuthUrl();
print "<a class='login' href='$authUrl'>Connect Me!</a>";
}
// Creation of a single event
$event = new Google_Event();
$event->setSummary($event_name);
$event->setLocation('');
....
?>
Thanks a lot for your support!
This page https://developers.google.com/accounts/docs/OAuth2WebServer#offline explains how the refresh token works, and how to use it to get a fresh access token using raw http.
From this question How to refresh token with Google API client? here is the php equivalent
here is the snippet to set token, before that make sure the access type should be set to offline
if (isset($_GET['code'])) {
$client->authenticate();
$_SESSION['access_token'] = $client->getAccessToken();
}
To refresh token
$google_token= json_decode($_SESSION['access_token']);
$client->refreshToken($google_token->refresh_token);
this will refresh your token, you have to update it in session for that you can do
$_SESSION['access_token']= $client->getAccessToken()
DEBUGGING
Follow these three steps to debug any oauth application
Make sure you have read https://developers.google.com/accounts/docs/OAuth2 and also the appropriate sub-page (webapp, javascript, etc) depending on which flavour of oauth you are using. You won't get very far debugging your app if you don't understand what it's doing.
Use the Oauth Playground at https://developers.google.com/oauthplayground/ to walk through the Oauth steps and ultimately make a Google API call. This will confirm your understanding and show you what the http calls and responses look like
Use tracing/logging/proxy etc to trace the actual http traffic from your app. Compare this with what you observed in step 2. This should show you where the problem lies.
I got this error before because I was trying to authenticate twice.
Since you have this line:
if (isset($_GET['code']))
It will try to authenticate when the code is in the query string regardless of whether you're already authenticated. Try checking whether the SESSION token is present before trying to (re)authenticate.
Try removing setDeveloperKey(), this was causing some issues for me and it seems unneeded.

How can I get an updated access token, using stored refresh token

I'm building an application that allows the admin to authenticate access to their analytics account for offline usage, and stores the refresh token in the database.
Now when I try to use the API on the frontend, it returns the following error:
"Access Token Expired. There wan a general error : The OAuth 2.0 access token has expired, and a refresh token is not available. Refresh tokens are not returned for responses that were auto-approved."
Here's my code that generates this error so far:
require_once "lib/google/Google_Client.php";
require_once "lib/google/contrib/Google_AnalyticsService.php";
$_analytics = new analytics();
$_googleClient = new Google_Client();
$_googleClient->setClientId($_analytics->gaClientId);
$_googleClient->setClientSecret($_analytics->gaClientSecret);
$_googleClient->setRedirectUri($_analytics->gaRedirectUri);
$_googleClient->setScopes($_analytics->gaScope);
$_googleClient->setAccessType($_analytics->gaAccessType);
// Returns last access token from the database (this works)
$_tokenArray['access_token'] = $_analytics->dbAccessToken($_agencyId);
$_googleClient->setAccessToken(json_encode($_tokenArray));
if($_googleClient->isAccessTokenExpired()) {
// Don't think this is required for Analytics API V3
//$_googleClient->refreshToken($_analytics->dbRefreshToken($_agencyId));
echo 'Access Token Expired'; // Debug
}
if (!$_googleClient->getAccessToken()) {
echo '<h2>Error - Admin has not setup analytics correct yet</h2>';
}
I'm after a function to run something like setRefreshToken - entering the value from the database, from the admin previously authenticating it online.
You could try the following, you would need to add in the code to store the new token in your database.
if($_googleClient->isAccessTokenExpired()) {
// Don't think this is required for Analytics API V3
//$_googleClient->refreshToken($_analytics->dbRefreshToken($_agencyId));
echo 'Access Token Expired'; // Debug
$_googleClient->authenticate();
$NewAccessToken = json_decode($_googleClient->getAccessToken());
$_googleClient->refreshToken($NewAccessToken->refresh_token);
}
Use a try/catch and use the catch for redirection/refresh of the access token.
Below is the solution I used for similar problem:
$plus = new Google_Service_Plus($client);
try {
$me = $plus->people->get('me');
} catch(Exception $e){
if(!(strpos($_SERVER["REQUEST_URI"],'logout'))){
if (isset($authUrl)){
$redirect = $authUrl;
}
else{
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] .'?logout';
}
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
exit;
}
You can replace the statement under try with the line throwing the exception, I guess that would be
$_googleClient->setClientId($_analytics->gaClientId);
or you can also try refreshing the token as per solution given here:
https://stackoverflow.com/a/22096740/1675384

Categories