I implemented Google OAuth2 for user login on my website.
It works but after 1 hour token expires and login fails.
I read on the web that I need to get the Refresh Token (for Facebook Login I used a Long Lived Token), but code I tried doesn't work.
Here the code:
//LOGIN CALLBACK FROM GOOGLE
$gClient = new Google_Client();
$gClient->setApplicationName(SITE_TITLE);
$gClient->setClientId(get_option('google_api_id')->value);
$gClient->setClientSecret(get_option('google_api_secret')->value);
$gClient->addScope('profile');
$gClient->addScope('email');
$gClient->setRedirectUri(SITE_URL."/login/google/google-callback.php");
if(isset($_GET['code'])) {
$gClient->setAccessType('offline');
$token = $gClient->fetchAccessTokenWithAuthCode($_GET['code']);
$gClient->setAccessToken($token['access_token']);
$_SESSION['google_access_token'] = $token['access_token'];
}
if($gClient->getAccessToken()) {
// Get user profile data from google
$google_oauthV2 = new Google_Service_Oauth2($gClient);
$gpUserProfile = $google_oauthV2->userinfo->get();
}
...
This first snippet works fine.
In this second snipped, when user change page, I verify if login is still active:
$gClient = new Google_Client();
$gClient->setApplicationName(SITE_TITLE);
$gClient->setClientId(get_option('google_api_id')->value);
$gClient->setClientSecret(get_option('google_api_secret')->value);
$gClient->addScope('profile');
$gClient->addScope('email');
$gClient->setAccessType('offline');
$gClient->setAccessToken($_SESSION['google_access_token']);
if($gClient->getAccessToken()) {
if ($gClient->isAccessTokenExpired()) {
$gClient->fetchAccessTokenWithRefreshToken($gClient->getRefreshToken());
}
$google_oauthV2 = new Google_Service_Oauth2($gClient);
$gpUserProfile = $google_oauthV2->userinfo->get();
...
}
This second snipped doesn't work, because method fetchAccessTokenWithRefreshToken($gClient->getRefreshToken()) fails because $gClient->getRefreshToken() is NULL.
I debugged the callback and I saw that $token = $gClient->fetchAccessTokenWithAuthCode returns an array without "refresh_token" field.
Can anyone help me?
Thanks
Bye
Related
I am a bit confused on the Access token. I have written a PHP script which inserts the data I get from the POST request , I already have authorized the App and it does add the Row at the end of the Sheet.
My Question is how I refresh the token when it get implemented on the server, as it will add the POST data.
Here is the Code
<?php
// Load the Google API PHP Client Library.
require_once __DIR__ . '/vendor/autoload.php';
session_start();
$client = new Google_Client();
$client->setAuthConfigFile(__DIR__ . '/client_secrets.json');
$client->addScope(Google_Service_Sheets::SPREADSHEETS);
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$lead = array (
"first_name" => $_POST['name'],
"email" => $_POST['email']
);
$sid = "sheet id on which the row is added";
addRowToSpreadsheet($lead, $client , $sid);
} else {
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/fb/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
function addRowToSpreadsheet($ary_values = array(), $client , $sid) {
$sheet_service = new Google_Service_Sheets($client);
$fileId = $sid;
$values = array();
foreach( $ary_values AS $d ) {
$cellData = new Google_Service_Sheets_CellData();
$value = new Google_Service_Sheets_ExtendedValue();
$value->setStringValue($d);
$cellData->setUserEnteredValue($value);
$values[] = $cellData;
}
// Build the RowData
$rowData = new Google_Service_Sheets_RowData();
$rowData->setValues($values);
// Prepare the request
$append_request = new Google_Service_Sheets_AppendCellsRequest();
$append_request->setSheetId(0);
$append_request->setRows($rowData);
$append_request->setFields('userEnteredValue');
// Set the request
$request = new Google_Service_Sheets_Request();
$request->setAppendCells($append_request);
// Add the request to the requests array
$requests = array();
$requests[] = $request;
// Prepare the update
$batchUpdateRequest = new Google_Service_Sheets_BatchUpdateSpreadsheetRequest(array(
'requests' => $requests
));
try {
// Execute the request
$response = $sheet_service->spreadsheets->batchUpdate($fileId, $batchUpdateRequest);
if( $response->valid() ) {
// Success, the row has been added
return true;
}
} catch (Exception $e) {
// Something went wrong
error_log($e->getMessage());
}
return false;
}
?>
I have tried hosting the app on the server and it doesn't add a new row in the Sheet, I think this is a problem due to the Access Token
Please Help
There is actually a PHP Quickstart for Sheets API which includes how to refresh tokens. Here's a snippet:
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
There's also a Refreshing an access token (offline access) guide with regard to refresh tokens
Access tokens periodically expire. You can refresh an access token
without prompting the user for permission (including when the user is
not present) if you requested offline access to the scopes associated
with the token.
If you use a Google API Client Library, the client object refreshes
the access token as needed as long as you configure that object for
offline access. If you are not using a client library, you need to set
the access_type HTTP query parameter to offline when redirecting the
user to Google's OAuth 2.0 server. In that case, Google's
authorization server returns a refresh token when you exchange an
authorization code for an access token. Then, if the access token
expires (or at any other time), you can use a refresh token to obtain
a new access token.
If your application needs offline access to a Google API, set the API
client's access type to offline:
$client->setAccessType("offline");
I have next code for google auth
use Google\Spreadsheet\DefaultServiceRequest;
use Google\Spreadsheet\ServiceRequestFactory;
require './vendor/autoload.php';
$application_name = '*****';
$client_secret = '**********';
$client_id = '**********.apps.googleusercontent.com';
$scope = array('https://spreadsheets.google.com/feeds');
$key = file_get_contents(__DIR__.'/the_key.txt');
$client = new Google_Client();
$client->setApplicationName($application_name);
$client->setClientId($client_id);
$client->setAccessType('offline');
$client->setAccessToken($key);
$client->setScopes($scope);
$client->setApprovalPrompt('force');
$client->setClientSecret($client_secret);
if ($client->getAccessToken()) {
if($client->isAccessTokenExpired()) {
$newToken = json_encode($client->getAccessToken());
$token = $client->getAccessToken();
$client->refreshToken($token['refresh_token']);
json_encode($client->getAccessToken()));
}
$key = json_decode(file_get_contents(__DIR__.'/the_key.txt'));
First time fillin file via oauthcallback via browser, and it works ok, i got refresh token and all works ok.
But when token expires as you see in code i request new via refresh_token, but in result i didn't get new refresh_token.
Also i forced acces type to offline, and approval prompt to force in Client.php of google-oauth-client(because even if i set it via paramaters it didn't worked).
My script must be executed via cron. So i can't each time receive token manually. Can you help me with this?
Recently I've decided to give users at my site the following functionality: online time slot booking in calendars of my employees. All employees have Google accounts linked to our domain in Google Apps (free edition). Users make booking in some front-end (e.g. not directly in employees Google calendars), then request is processed at our PHP server and if it is correct, server should be able to create new calendar entry in selected employee Google calendar. Note - neither user, nor employee should not be asked for authentication during booking.
I've scanned Google calendar v3 API and forums and still didn't get neither clear answer not concise examples - is such scenario possible with Google calendar? Can someone help me to answer the Q (and if possible - share a link with proper example)?
Are you familiar with adding an event with authentication? Like i answered here.
And like in the code below. If you are... you can go to the next level ;)
(You can download the Google Api Php-client on this page. On this page there is a tutorial. It's for Google+ but the principle is the same)
The first time you would always need authentication. There is no way around that. In the example below you get a token back after authentication, which includes an access_token and a refresh_token. The access_token is only valid for 3600 seconds and is used for direct access. When the access_token is expired you get a 401 error and you can use the refresh_token (together with client_id, client_secret and the correct grant_type) to request a new access_token.
You can find some more information on this page (at the bottom "Using a Refresh Token"). There are some limits in numbers to request these, mentioned on that page at the bottom.
This is some testing code i made the last time i helped someone with this. It only shows getting the authentication and storing the token in a cookie. It does not request a new access_token after it expires.
But as i already said you need to store the token (in a database?) and when you get a 401 you need to request a new access_token with the client_id, client_secret, correct grant_type and refresh_token. There is no (re-)authentication necessary for that.
<?php
error_reporting(E_ALL);
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_CalendarService.php';
session_start();
if ((isset($_SESSION)) && (!empty($_SESSION))) {
echo "There are cookies<br>";
echo "<pre>";
print_r($_SESSION);
echo "</pre>";
}
$client = new Google_Client();
$client->setApplicationName("Google Calendar PHP Starter Application");
$client->setClientId('###');
$client->setClientSecret('###');
$client->setRedirectUri('http://###/add_calendar.php'); // <- registered web-page
//$client->setDeveloperKey('###'); // <- not always needed
$cal = new Google_CalendarService($client);
if (isset($_GET['logout'])) {
echo "<br><br><font size=+2>Logging out</font>";
unset($_SESSION['token']);
}
if (isset($_GET['code'])) {
echo "<br>I got a code from Google = ".$_GET['code']; // You won't see this if redirected later
$client->authenticate(); // $_GET['code']
$_SESSION['token'] = $client->getAccessToken();
header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF']);
// not strictly necessary to redirect but cleaner for the url in address-bar
echo "<br>I got the token = ".$_SESSION['token']; // <-- not needed to get here unless location uncommented
}
if (isset($_SESSION['token'])) {
echo "<br>Getting access";
$client->setAccessToken($_SESSION['token']);
}
if ($client->getAccessToken()){
echo "<hr><font size=+1>I have access to your calendar</font>";
$event = new Google_Event();
$event->setSummary('=== I ADDED THIS ===');
$event->setLocation('The Neighbourhood');
$start = new Google_EventDateTime();
$start->setDateTime('2013-11-29T10:00:00.000-05:00');
$event->setStart($start);
$end = new Google_EventDateTime();
$end->setDateTime('2013-11-29T10:25:00.000-05:00');
$event->setEnd($end);
$createdEvent = $cal->events->insert('###', $event); // <- ### = email of calendar
echo "<br><font size=+1>Event created</font>";
echo "<hr><br><font size=+1>Already connected</font> (No need to login)";
} else {
$authUrl = $client->createAuthUrl();
print "<hr><br><font size=+2><a href='$authUrl'>Connect Me!</a></font>";
}
$url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
echo "<br><br><font size=+2><a href=$url?logout>Logout</a></font>";
?>
Edit:
If you don't want the initial authentication Google has its "Service Acounts", as you pointed out.
(I didn't know about these :)
I dug up some links where you can find code to use after you got the key-file.
Edit Google calendar events from Google service account: 403
Access Google calendar events from with service account: { "error" : "access_denied" }. No google apps
Google OAuth 2.0 Service Account - Calendar API (PHP Client)
https://groups.google.com/forum/#!topic/google-api-php-client/B7KXVQvx1k8
https://groups.google.com/forum/?fromgroups#!topic/google-api-php-client/IiwRBKZMZxw
Edit #2: YES, got it working. (even with my free Google account ;)
Here is the working code:
<?
error_reporting(E_ALL);
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_CalendarService.php';
session_start();
echo "Busy<br>";
const CLIENT_ID = 'xxxxxxxxxx.apps.googleusercontent.com';
const SERVICE_ACCOUNT_NAME = 'xxxxxxxxxxx#developer.gserviceaccount.com';
const KEY_FILE = 'xxxxxxxxxxxxxxxxxxx-privatekey.p12';
const CALENDAR_NAME = 'xxxxxxx#gmail.com';
$client = new Google_Client();
$client->setApplicationName("mycal");
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
$key = file_get_contents(KEY_FILE);
$client->setClientId(CLIENT_ID);
$client->setAssertionCredentials(new Google_AssertionCredentials(
SERVICE_ACCOUNT_NAME,
array('https://www.google.com/calendar/feeds/'),
$key)
);
$client->setClientId(CLIENT_ID);
$service = new Google_CalendarService($client);
$event = new Google_Event();
$event->setSummary('=== I MADE THIS ===');
$event->setLocation('Somewhere else');
$start = new Google_EventDateTime();
$start->setDateTime('2013-11-30T10:00:00.000-02:00');
$event->setStart($start);
$end = new Google_EventDateTime();
$end->setDateTime('2013-11-30T10:25:00.000-02:00');
$event->setEnd($end);
$createdEvent = $service->events->insert(CALENDAR_NAME, $event);
echo "Done<br>";
?>
I needed to make a "new project" on https://cloud.google.com/console#/project. I couldn't use the existing one. After activating "Calendar API" and registering a new Web-app with Certificate i only needed to share my calendar with the xxxx#developer.gserviceaccount.com and above code worked
(without authentication).
Edit #3: and now it also seems to be working in the default "Google+ API Project"
Hi I'm making an app which post tweets to users time lines after they have authenticated. I'm using the library and tutorial found here : twitteroauth
Everything works fine until I get to the callback url and attempt to retrieve the access token and a var dump of the request returns Invalid request token .
here's the code :
function authenicateApp() {
//Set app keys
// Connect to twitter and recive token
$connection = new TwitterOAuth('**', '**');
$temporary_credentials = $connection->getRequestToken('http://abc.def.com');
// Redirect to twitter authentation page
header('location:'.$connection->getAuthorizeURL($temporary_credentials));
}
function appPost() {
$connection1 = new TwitterOAuth('**' ,'**', $_SESSION['oauth_token'],
$_SESSION['oauth_token_secret']);
var_dump($connection1->getAccessToken($_REQUEST['oauth_verifier']));
}
You have to actually have $temporary_credentials to the session before you try and use the session when you create $connection1.
Im using the following code to read to consumer_key and consumer_secret from config.php, pass it to twitter and retrieve some bits of information back from them.
What the script below attempts to do is 'cache' the request_token and request_secret. So in theory I should be able to reuse those details (all 4 of them to automatically tweet when required).
<?php
require_once('twitteroauth/twitteroauth.php');
require_once('config.php');
$consumer_key = CONSUMER_KEY;
$consumer_secret = CONSUMER_SECRET;
if (isset($_GET["register"]))
{
// If the "register" parameter is set we create a new TwitterOAuth object
// and request a token
/* Build TwitterOAuth object with client credentials. */
$oauth = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET);
$request = $oauth->getRequestToken();
$request_token = $request["oauth_token"];
$request_token_secret = $request["oauth_token_secret"];
// At this I store the two request tokens somewhere.
file_put_contents("request_token", $request_token);
file_put_contents("request_token_secret", $request_token_secret);
// Generate a request link and output it
$request_link = $oauth->getAuthorizeURL($request);
echo "Request here: " . $request_link . "";
die();
}
elseif (isset($_GET["validate"]))
{
// This is the validation part. I read the stored request
// tokens.
$request_token = file_get_contents("request_token");
$request_token_secret = file_get_contents("request_token_secret");
// Initiate a new TwitterOAuth object. This time we provide them with more details:
// The request token and the request token secret
$oauth = new TwitterOAuth($consumer_key, $consumer_secret,
$request_token, $request_token_secret);
// Ask Twitter for an access token (and an access token secret)
$request = $oauth->getAccessToken();
// There we go
$access_token = $request['oauth_token'];
$access_token_secret = $request['oauth_token_secret'];
// Now store the two tokens into another file (or database or whatever):
file_put_contents("access_token", $access_token);
file_put_contents("access_token_secret", $access_token_secret);
// Great! Now we've got the access tokens stored.
// Let's verify credentials and output the username.
// Note that this time we're passing TwitterOAuth the access tokens.
$oauth = new TwitterOAuth($consumer_key, $consumer_secret,
$access_token, $access_token_secret);
// Send an API request to verify credentials
$credentials = $oauth->oAuthRequest('https://twitter.com/account/verify_credentials.xml', 'GET', array());
// Parse the result (assuming you've got simplexml installed)
$credentials = simplexml_load_string($credentials);
var_dump($credentials);
// And finaly output some text
echo "Access token saved! Authorized as #" . $credentials->screen_name;
die();
}
?>
When i run /?verify&oauth_token=0000000000000000 - It works however trying to resuse the generated tokens etc... I get a 401
Here is the last bit of code where I attempt to reuse the details from Twitter combined with my consumer_key and consumer_secret and get the 401:
require_once('twitteroauth/twitteroauth.php');
require_once('config.php');
// Read the access tokens
$access_token = file_get_contents("access_token");
$access_token_secret = file_get_contents("access_token_secret");
// Initiate a TwitterOAuth using those access tokens
$oauth = new TwitterOAuth($consumer_key, $consumer_key_secret,
$access_token, $access_token_secret);
// Post an update to Twitter via your application:
$oauth->OAuthRequest('https://twitter.com/statuses/update.xml',
array('status' => "Hey! I'm posting via #OAuth!"), 'POST');
Not sure whats going wrong, are you able to cache the details or do i need to try something else?
You can't store the OAuth tokens in cache and to more than 1 request with it, as OAuth is there to help make the system secure, your "oauth_token" will contain some unique data, this token will only be able to make one call back to twitter, as soon as the call was made, that "oauth_token" is no longer valid, and the OAuth class should request a new "oauth_token", thus making sure that every call that was made is secure.
That is why you are getting an "401 unauthorized" error on the second time as the token is no longer valid.
twitter is still using OAuth v1 (v2 is still in the draft process even though facebook and google already implemented it in some parts)
The image below describes the flow of the OAuth authentication.
Hope it helps.
A while ago I used this to connect to twitter and send tweets, just note that it did make use of some Zend classes as the project was running on a zend server.
require_once 'Zend/Service/Twitter.php';
class Twitter {
protected $_username = '<your_twitter_username>';
protected $_token = '<your_twitter_access_token>';
protected $_secret = '<your_twitter_access_token_secret>';
protected $_twitter = NULL;
//class constructor
public function __construct() {
$this->getTwitter();
}
//singleton twitter object
protected function getTwitter() {
if (null === $this->_twitter) {
$accessToken = new Zend_Oauth_Token_Access;
$accessToken->setToken($this->_token)
->setTokenSecret($this->_secret);
$this->_twitter = new Zend_Service_Twitter(array(
'username' => $this->_username,
'accessToken' => $accessToken,
));
$response = $this->_twitter->account->verifyCredentials();
if ($response->isError()) {
throw new Zend_Exception('Provided credentials for Twitter log writer are wrong');
}
}
return $this->_twitter;
}
//send a status message to twitter
public function update( $tweet ) {
$this->getTwitter()->status->update($tweet);
}
}
In your second script, it looks like you aren't setting the Consumer Key and Consumer Secret when you create your TwitterOAuth instance.
// Initiate a TwitterOAuth using those access tokens
$oauth = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $access_token, $access_token_secret);