Does the contacts API still work for anyone? A while back I started getting 403 errors from the API. I run my backup script once a week. Every user that I can't back up, I retry every hour until the next week. Over the span of the week I will end up with a couple of contacts entries, but not a significant amount. This leads me to believe that my code still works since I get some contacts. Anyone have any insight?
Note: I also use the same code/framework to backup Google Drive and Google Calendars for my organization and have not had any issues.
function retrieveAllUserContacts($user)
{
$nextLink = "https://www.google.com/m8/feeds/contacts/$user/full";
$params = array('xoauth_requestor_id' => $user);
while($nextLink != '')
{
$header = array('GData-Version: 3.0');
$result = sendOAuthRequest('GET', $nextLink, $params, $header);
$params = array('xoauth_requestor_id' => $user);
$nextLink = '';
libxml_use_internal_errors(true);
$xmlObj = simplexml_load_string($result);
if($xmlObj === false)
{
echo "adding $user to retry list. Result : " . print_r($result, true) . "\n";
addUserToRetryList($user);
exit(1);
}
foreach($xmlObj->link as $link)
{
if($link['rel'] == 'next')
{
$nextLink = $link['href'];
}
if($nextLink != '')
{
$urlSplit = explode('?', $nextLink);
$nextLink = $urlSplit[0];
$urlParams = explode('&', $urlSplit[1]);
foreach($urlParams as $urlParam)
{
$urlParamSplit = explode('=', $urlParam);
$params[$urlParamSplit[0]] = $urlParamSplit[1];
}
break;
}
}
foreach($xmlObj->entry as $entry)
{//get contacts
...
}
function sendOAuthRequest($httpMethod, $url, $parameters, $header=array())
{
global $CONSUMER_KEY;
global $CONSUMER_SECRET;
$consumer = new OAuthConsumer($CONSUMER_KEY, $CONSUMER_SECRET, NULL);
$request = OAuthRequest::from_consumer_and_token($consumer, NULL, $httpMethod, $url, $parameters);
// Sign the constructed OAuth request using HMAC-SHA1
$request->sign_request(new OAuthSignatureMethod_HMAC_SHA1(), $consumer, NULL);
// Make signed OAuth request to the Contacts API server
if(count($parameters))
{
if(strpos($url, '?') === false)
{
$url .= '?' . implode_assoc('=', '&', $parameters);
}
else
{
$url .= '&' . implode_assoc('=', '&', $parameters);
}
}
$header[] = $request->to_header();
return send_request($request->get_normalized_http_method(), $url, $header);
}
Here is the OAuth Class code : http://pastebin.com/hH4SM9nn I can say that it works for Google Drive and Google Calendar APIs and also that this code worked for over a year without issue.
A number of problems could be causing this. Since your question isn't specific and gives no code sample, here are some of those potential problems:
You have not moved to HTTPS as opposed to HTTP
your GET request specifies a "default" user, where the authority is not always given
You are trying to get too many contacts
There are also issues with authorisation and permissions as of late, one "fix" as such is documented in this StackOverflow answer. It concerns adding not only a google account, but the gmail address associated with that account to the analytics service, via this link.
Related
I want to read draft grade marks from Google Classroom using API for a project. But I can't find out the draft grade. Already I've added some code to the quickstart.php file:
require __DIR__ . '/vendor/autoload.php';
// if (php_sapi_name() != 'cli') {
// throw new Exception('This application must be run on the command line.');
// }
/**
* Returns an authorized API client.
* #return Google_Client the authorized client object
*/
function getClient()
{
$client = new Google_Client();
$client->setApplicationName('Google Classroom API PHP Quickstart');
$client->setScopes(array(
Google_Service_Classroom::CLASSROOM_COURSES,
Google_Service_Classroom::CLASSROOM_STUDENT_SUBMISSIONS_STUDENTS_READONLY,
Google_Service_Classroom::CLASSROOM_ROSTERS)
);
$client->setAuthConfig(__DIR__ .'/credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
$optParams = array(
'pageSize' => 1000
);
// Copyright 2021 Google LLC.
// SPDX-License-Identifier: Apache-2.0
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Classroom($client);
// set these parameters:
// 328776504166 <- It is my course id
// 339429593407 <- It is my course work id
$courseId = "328776504166";
$courseWorkId = "339429593407";
$results = $service->courses_courseWork_studentSubmissions->listCoursesCourseWorkStudentSubmissions($courseId, $courseWorkId);
foreach ($results->studentSubmissions as $r => $submission) {
$student = $service->courses_students->get($courseId, $submission->userId);
$studentName = $student->profile->name->fullName;
print("<br>Student Name: ".$studentName . ": ");
print("<br>Draft Grade: ".$submission->draftGrade. "\n");
print("<br>Course Work Id: ".$submission->courseWorkId. "\n");
echo '<pre>';
print_r($submission);
}
Then when I run quickstart.php at localhost the following problems can be seen:
Fatal error: Uncaught Google_Service_Exception: { "error": { "code": 400, "message": "Precondition check failed.", "errors": [ { "message": "Precondition check failed.", "domain": "global", "reason": "failedPrecondition" } ], "status": "FAILED_PRECONDITION" } } in C:\xampp\htdocs\api\vendor\google\apiclient\src\Google\Http\REST.php:118 Stack trace: #0
I can't find my wrong. How to solve this problem? please give me some suggestions.
As stated in the documentation:
Students cannot see draft grades
This is why a student who intents to retrieve a draft grade will obtain a
403 - "Insufficient Permission" error.
I want to obtain users' phone numbers via Google sign-in on my website. In JavaScript for the "sign in with Google" button, I'm including scope 'https://www.googleapis.com/auth/user.phonenumbers.read' for permission to read the user's phone number. Maybe instead of this scope, I need to use 'https://www.googleapis.com/auth/contacts.readonly'. In any case, how do I obtain a signed-in user's phone number in PHP or JavaScript? When a user clicks on the sign-in button then because of the scope Google does ask permission to share a phone number. In Google API Console -> Edit app registration -> Scopes, I've included this phone number scope. Also, I've enabled People API in the Google project. I've installed
composer require google/apiclient
From the front end i'm receiving id-token for the signed-in user. My PHP is:
<?php
require_once 'vendor/autoload.php';
$id_token = $_POST['idtoken'];
$client = new Google_Client(['client_id' => '349001386451-bpovja3t7soabdu3cbhnig12fqlr20o0.apps.googleusercontent.com']);
$payload = $client->verifyIdToken($id_token);
if ($payload) {
$userid = $payload['sub'];
echo "Userid: $userid";
} else {
echo "Invalid ID token";
}
( The above code has been edited from https://developers.google.com/identity/sign-in/web/backend-auth )
I'm a newbie to this. I've got my client-id, client-secret and user's id-token. I'm able to show the userid in the above code, how to display the phone number?
Edit: I downloaded my client_secret.json and tried another method:
index.php
<?php
require_once __DIR__.'/vendor/autoload.php';
session_start();
$client = new Google\Client();
$client->setAuthConfig('client_secret.json');
$client->setScopes(array('https://www.googleapis.com/auth/user.phonenumbers.read', 'https://www.googleapis.com/auth/contacts.readonly', 'profile'));
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$service = new Google_Service_PeopleService( $client );
$optParams = ['personFields' => 'phoneNumbers'];
$profile = $service->people->get( 'people/me', $optParams );
var_export($profile);
var_export( $profile->getPhoneNumbers() );
} else {
$redirect_uri = 'https://' . $_SERVER['HTTP_HOST'] . '/testing/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
--
oauth2callback.php
<?php
require_once __DIR__.'/vendor/autoload.php';
session_start();
$client = new Google\Client();
$client->setAuthConfigFile('client_secret.json');
$client->setRedirectUri('https://' . $_SERVER['HTTP_HOST'] . '/testing/oauth2callback.php');
$client->addScope(Google_Service_PeopleService::USER_PHONENUMBERS_READ);
if (! isset($_GET['code'])) {
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
$redirect_uri = 'https://' . $_SERVER['HTTP_HOST'] . '/testing/';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
But when I'm running index.php it's giving error:
"error": { "code": 403, "message": "The caller does not have
permission to request "people/me". Request requires one of the
following scopes: [profile]."
But I do have included the profile scope in index.php
The phone number used for password reset will not be possible access:
It has been determined that we will not return the account recovery
phone number. The account recovery phone number is only intended for
specific usage like recovery the account when locked out. In the
interest of protecting user privacy this will not be returned in the
3rd party API.
I'm successfully getting phone number using a new 3rd method as given here:
https://developers.google.com/people/api/rest/v1/people/get?apix=true&apix_params=%7B%22resourceName%22%3A%22people%2Fme%22%2C%22personFields%22%3A%22phoneNumbers%22%7D
I copied the JavaScript code given in this link, removed all scopes except one, replaced YOUR_API_KEY and YOUR_CLIENT_ID, ran it on my server, in Firefox and it worked!
<script src="https://apis.google.com/js/api.js"></script>
<script>
/**
* Sample JavaScript code for people.people.get
* See instructions for running APIs Explorer code samples locally:
* https://developers.google.com/explorer-help/guides/code_samples#javascript
*/
function authenticate() {
return gapi.auth2.getAuthInstance()
.signIn({scope: "https://www.googleapis.com/auth/user.phonenumbers.read"})
.then(function() { console.log("Sign-in successful"); },
function(err) { console.error("Error signing in", err); });
}
function loadClient() {
gapi.client.setApiKey("YOUR_API_KEY");
return gapi.client.load("https://people.googleapis.com/$discovery/rest?version=v1")
.then(function() { console.log("GAPI client loaded for API"); },
function(err) { console.error("Error loading GAPI client for API", err); });
}
// Make sure the client is loaded and sign-in is complete before calling this method.
function execute() {
return gapi.client.people.people.get({
"resourceName": "people/me",
"personFields": "phoneNumbers"
})
.then(function(response) {
// Handle the results here (response.result has the parsed body).
console.log("Response", response);
},
function(err) { console.error("Execute error", err); });
}
gapi.load("client:auth2", function() {
gapi.auth2.init({client_id: "YOUR_CLIENT_ID"});
});
</script>
<button onclick="authenticate().then(loadClient)">authorize and load</button>
<button onclick="execute()">execute</button>
But it only reads the phone number(s) added in Google account's "About me": https://myaccount.google.com/profile
And not the phone number of Google account which is used for password reset. I actually want this number but don't know whether possible.
I'm trying to decrypt laravel_session but the following error occur:
An error has occurred: The payload is invalid.
simply I'm using Rachet and I'm trying to call the authorized user, so I got the cookies in httpRequest using the following:
public function onOpen(ConnectionInterface $conn) {
$this->clients[$conn->resourceId] = new Client();
$this->clients[$conn->resourceId]->conn = $conn;
$cookiesRaw = $conn->httpRequest->getHeader('Cookie');
$cookies = [];
if(count($cookiesRaw))
{
$cookies = \GuzzleHttp\Psr7\parse_header($cookiesRaw)[0]; // Array of cookies
}
// Get the laravel's one
$laravelCookie = $cookies[Config::get('session.cookie')];
$idSession = Crypt::decrypt($laravelCookie);
echo "\n cookie is ";
print_r($idSession);
}
The Crypt::decrypt($laravelCookie); is causing the error, I've tried to use:
$cookie_contents = json_decode( base64_decode( $laravelCookie, true ));
$value = base64_decode( $cookie_contents->value );
$iv = base64_decode( $cookie_contents->iv );
$clear = unserialize( \openssl_decrypt($value, \Config::get( 'app.cipher' ), \Config::get( 'app.key' ), OPENSSL_RAW_DATA, $iv));
echo "Cookie contents (Session ID): $value\n";
but it is also causing The payload is invalid.
How can I decrypt this laravel_session in order to get user session id !
I want to get the Auth user via this session.
I've searched a lot in stack-overflow and google, but all the cases didn't match mine.
Note: I'm using SESSION_DRIVER=file
Laravel provides a way to grab your session id:
use Illuminate\Support\Facades\Session;
$sessionid = Session::getId();
I have created a custom module in Drupal 8 that allows a user to authenticate using facebook login. Their access token is checked against one stored in the database and if it matches authenticates the user and if it doesn't then redirects them to a page that allows them to link their Facebook account to a Drupal user.
The button for login is:
<button id="login_fb" onclick="logIt()">Log in with Facebook</button>
The "logit" function with the ajax request to the Drupal controller is:
function logIt()
{
FB.login(function(response) {
if (response.authResponse) {
if(response.authResponse.accessToken)
{
var request = $.ajax({
url: "/user/token",
method: "POST",
data: { access_token : response.authResponse.accessToken},
dataType: "json"
});
request.done(function( msg ) {
window.location.replace(msg['redirect_url']);
});
request.fail(function( jqXHR, textStatus ) {
alert( "Request failed: " + textStatus );
});
}
}
}
And the controller code that handles this ajax call is:
public function token() {
$fb_token = $_POST['access_token'];
$query = db_select('user__field_fb_token', 'u');
$query
->fields('u')
->condition('u.field_fb_token_value', $fb_token,'=');
$res = $query->execute();
$res->allowRowCount = TRUE;
$count = $res->rowCount();
//See if anybody has this access token
if($count > 0)
{
$user = $res->fetchAssoc();
//TODO: Refresh access token and update
$login_id = $user['entity_id'];
//Redirect the user to topics
user_login_finalize(user_load($login_id));
$response_arr = array("status" => "authorised","redirect_url" => "/topics");
}
else
{
$_SESSION['access_token'] = $fb_token;
$response_arr = array("status" => "unauthorised","redirect_url" => "/user/auth","token" => $fb_token);
}
$response = new Response();
$response->setContent(json_encode($response_arr));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
The weird thing is that the db_select query always returns 0 and therefore does not authenticate the user account that has this token. However replacing
$fb_token = $_POST['access_token'];
with
$fb_token = '** hard coded access token **';
yields the correct result. I have checked that the post variable being passed in and it is present (that's why I pass it back with the unauthorised response to check that it's not blank).
I think it may have something to do with the async nature of FB.Login method but not sure.
Any help on this matter would be greatly appreciated!
I'm new to CodeIgniter. I'm using Phil Sturgeon's RestServer and RestClient. I've been trying to make a POST request in my CodeIgniter RestClient controller to update data in my CodeIgniter RestServer, but it never updates data in my database. I think my POST request is not right.
Here is my RestClient POST request in controller:
$result = $this->rest->post('newscontroller/news/format/json',
array('news_id' => $news_id,
'news_title' => $news_title,
'news_category' => $news_category ),
'json');
if(isset($result->status) && $result->status == 'success')
{
$data['message'] ='News has been updated.';
$this->load->view('otherpageview',$data);
}
else
{
$data['message'] ='Something has gone wrong';
$this->load->view('otherpageview',$data);
}
It seems that $result doesn't get any value, because I did echo the $result->status and it has nothing to display. And I've also have this in this controller's constructor :
// Load the rest client spark
$this->load->spark('restclient/2.1.0');
// Load the library
$this->load->library('rest');
// Run some setup
$this->rest->initialize(array('server' => 'http://api.therestserver.com/index.php/'));
And in the RestServer's controller, which is newscontroller, has this method :
function news_post()
{
$news=array(
'news_id' => $this->post('news_id'),
'news_title' => $this->post('news_title'),
'news_category' => $this->post('news_category') );
$result = $this->News_model->UpdateNews($news);
if($result === FALSE)
{
$this->response(array('status' => 'failed'));
}
else
{
$this->response(array('status' => 'success'));
}
}
With the News_model :
public function UpdateNews($news)
{
$this->db->where('news_id',$news->news_id);
$this->db->update('news',$news);
}
I just don't know where I'm doing wrong, because I still don't understand how the POST request and method work. I've read through the tutorial in Nettuts and search about this, but still.. maybe because of my bad English reading-writing. I hope someone can help me out, any help would be appreciated. Thanks a TON! :)
Finally SOLVED this problem!
It was my POST request in the RESTClient controller that is wrong. After doing some searching and lots of trying / changing the codes, this code works for me for POST request in my RESTClient controller :
$news_id = 12; //this is the id of the news that you want to edit
$method = 'post';
$params = array('news_id' => $news_id,
'news_title' => $this->input->post('news_title'),
'news_category' => $this->input->post('news_category') );
$uri = 'newscontroller/news';
$this->rest->format('application/json');
$result = $this->rest->{$method}($uri, $params);
if(isset($result->status) && $result->status == 'success')
{
$data['message'] ='News has been updated.';
$this->load->view('otherpageview',$data);
}
else
{
$data['message'] ='Something has gone wrong';
$this->load->view('otherpageview',$data);
}
With a lot of help from this reference
I post this if anybody needs an example of the right POST request in RESTClient and RESTServer, because I find it hard to look for an example for POST request in RESTClient-Server*** by Phil Sturgeon.
I'm using :
RESTServer (philsturgeon) v.2.6.0
RESTClient (philsturgeon) v.2.1.0
cURL (philsturgeon) v.1.2.1
CodeIgniter v.2.0.3
There was a problem with the way the post was implemented. see issue on github and another issue on github.
You can either patch your code, or get the latest sources.
Basically you find the post function in the RestServer application/libraries/REST_Controller.php and if it does not look like the following then change it to:
public function post($key = NULL, $xss_clean = TRUE)
{
if ($key === NULL)
{
return $this->_post_args;
}
return array_key_exists($key, $this->_post_args) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : FALSE;
}