Getting Google Contacts using LightOpenID? - php

I am currently using LightOpenID to allow users to log into my site, where I can automatically extract their username and email address:
$openid->required = array('namePerson/first', 'namePerson/last', 'contact/email');
$openid->identity = 'https://www.google.com/accounts/o8/id';
Here I am using the parameters namePerson/first, namePerson/last, and contact/email.
I understand that inorder to get a list of user contacts, I have to use the feed:
https://www.google.com/m8/feeds
However, I can't seem to figure out which parameters I need to use for this?
If I remove the paramter line altogether, I just get an empty array back.
Can anyone please help me figure out which parameters I need to get the contacts?
Here is the current code I have:
<?php
require '/var/www/libraries/openid.php';
try {
$openid = new LightOpenID;
if(!$openid->mode) {
//$openid->required = array('gd/fullName');
$openid->identity = 'https://www.google.com/m8/feeds/contacts/oshirowanen.y%40gmail.com/full';
header('Location: ' . $openid->authUrl());
exit;
} elseif($openid->mode == 'cancel') {
echo "cancelled";
exit;
} else {
if ( $openid->validate() ) {
$returned = $openid->getAttributes();
print_r($returned);
exit;
} else {
echo "something is wrong";
exit;
}
}
} catch(ErrorException $e) {
echo $e->getMessage();
}
?>

You can't do that with LightOpenID because it only implements the OpenID protocol.
You will need the OAuth (2.0) protocol to do that. Per the docs:
About authorization protocols
We recommend using OAuth 2.0 to authorize requests.
If your application has certain unusual authorization requirements,
such as logging in at the same time as requesting data access (hybrid)
or domain-wide delegation of authority (2LO), then you cannot
currently use OAuth 2.0 tokens. In such cases, you must instead use
OAuth 1.0 tokens and an API key. You can find your application's API
key in the Google API Console, in the Simple API Access section of the
API Access pane.

Per the docs:
Retrieving all contacts
To retrieve all of a user's contacts, send an authorized GET request
to the following URL:
https://www.google.com/m8/feeds/contacts/{userEmail}/full
With the appropriate value in place of userEmail.
Note: The special userEmail value default can be used to refer to the
authenticated user.

It should be possible as per the docs:
https://developers.google.com/accounts/docs/OpenID
OpenID+OAuth Hybrid protocol lets web developers combine an OpenID request with an OAuth
authentication request. This extension is useful for web developers who use both OpenID and OAuth, particularly in that it simplifies the process for users by requesting their approval once instead of twice.

Related

Google Gmail API - How to login programatically?

I'm looking to write a PHP script that scans my gmail inbox, and reads unread emails. There needs to be NO user interaction. This has to happen on a cronjob that executes a PHP file.
Is this even possible with the API? Googles documentation is absolutely terrible, and no-where does there seems to be any examples that allow you to authorize a log in programatically. They always require a user to physically press the allow button on an oauth request.
Has anybody got experience in trying to simply login and list your messages, without the need of human interaction?
client login
I think what you are trying to ask here is how to login to the api using your login and password. The anwser is you cant this was called client login and google shut down that option in 2015. You have not choice but to use the Oauth2 if you want to connect to the gmail api
service accounts
Normally i would say that you should use a service account. However service accounts only work with gmail if you have a gsuite account in which case you can set up domain wide delegation's here
// Load the Google API PHP Client Library.
require_once __DIR__ . '/vendor/autoload.php';
// Use the developers console and download your service account
// credentials in JSON format. Place the file in this directory or
// change the key file location if necessary.
putenv('GOOGLE_APPLICATION_CREDENTIALS='.__DIR__.'/service-account.json');
/**
* Gets the Google client refreshing auth if needed.
* Documentation: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
* Initializes a client object.
* #return A google client object.
*/
function getGoogleClient() {
return getServiceAccountClient();
}
/**
* Builds the Google client object.
* Documentation: https://developers.google.com/api-client-library/php/auth/service-accounts
* Scopes will need to be changed depending upon the API's being accessed.
* array(Google_Service_Analytics::ANALYTICS_READONLY, Google_Service_Analytics::ANALYTICS)
* List of Google Scopes: https://developers.google.com/identity/protocols/googlescopes
* #return A google client object.
*/
function getServiceAccountClient() {
try {
// Create and configure a new client object.
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope([YOUR SCOPES HERE]);
return $client;
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
}
oatuh2
In the event that you are not using gsuite. Then what you can do is authenticate your code once. Make sure to request off line access. A refresh token will be returned to you. If you save this refresh token you can then use that refresh token at anytime to request a new access token. In the example below you can see how the refresh token was simply stored in a session varable you could store it in a file and read from that when ever you need.
function getOauth2Client() {
try {
$client = buildClient();
// Set the refresh token on the client.
if (isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']) {
$client->refreshToken($_SESSION['refresh_token']);
}
// If the user has already authorized this app then get an access token
// else redirect to ask the user to authorize access to Google Analytics.
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
// Set the access token on the client.
$client->setAccessToken($_SESSION['access_token']);
// Refresh the access token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$client->setAccessToken($client->getAccessToken());
$_SESSION['access_token'] = $client->getAccessToken();
}
return $client;
} else {
// We do not have access request access.
header('Location: ' . filter_var( $client->getRedirectUri(), FILTER_SANITIZE_URL));
}
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
}
code ripped from Oauth2Authentication.php
smtp
You mentioned that you want to use the gmail api but have you considered going directly though the mail server? This would allow you to use login and password. or oauth -> Imap-smtp
You need to use three-legged OSuth, that is OAuth where the end-user (owner of the gmail account), logs in and authorizes your app to read their email. There is no other way to access a user's Gmail account via the API except with three-legged OAuth.
The end user needs to click on the first time. Once your app has received consent from the end user, the app can access the Gmail API on behalf of the user in the future without clicks. I find this documentation the clearest, look for grantOfflineAccess().
You could copy and paste a simple JavaScript Frontend from the documentation that allows you to do the log in, and write your backend logic in PHP.

How to use OAuth2 Browser based authentication, and then verify the record on the server

I have a browser-based app (single page, AngularJS) and am using hello to use third party signin such as Google, FB, Soundcloud, etc.
My app uses a PHP API server.
What's a good way to have the user able to login using Google, but also verify the user on the server side?
I was considering:
The browser app performs an implicit grant with google/fb/etc
I then transfer the access_token from the client to the server, then use, for example, a google-api-php-client with my app id, secret and the user access_token? Using their API such as /me? (which grant type would this be?)
Retrieve some key from the third-party (facebook_id, email, etc), match it against a user in my database, and then consider the user authenticated?
Also, should I perform this on each API request? Or should I just stash the access_token for a bit and assume that the user is still valid until the key expires?
One issue is that not all of those providers support the implicit flow. But assuming they do, the access_token you get for each will be proof that the user authenticated with that system, not necessarily that they have access to call your API. You still need something that asserts that "someone#gmail.com can 'read' resource X in your system"
You probably need something that translates whatever you get from Google, Soundcloud, etc. into a token your app understands. A simple(r) format is to use JWT. (Json Web Tokens).
App -> Intermmediary -> Soundcloud/Google
<-JWT--+ <---whavetever-+
and then:
App - (JWT) -> API
JWT are easy to manipulate, validate and verify. See jwt.io
You might want to look at this blog post also for some additional information (specifically on AngularJS front-ends)
The blog post #eugenio-pace mentioned was really helpful for setting up the client side.
For the server side though, the access_token should be validated.
The SDK's are (in composer) (code below):
Facebook: "facebook/php-sdk-v4" : "4.0.*"
Google: cURL request (didn't care for "google/apiclient")
SoundCloud: "ise/php-soundcloud": "3.*"
(There are others of course, just these three were the ones I chose, and seem decent.)
Last time I did something like this I made the mistake of validating the access_token on every request, which had a huge (obviously negative) impact on performance. Now I just validate it on login and use it to retrieve the user's ID from that service. So, the browser sends me access_token A and says it's from Facebook, I use the sdk above the the access_token with Facebook, and I get back their ID so I know they are who they say they are.
I'd suggest storing the access_token on the server with the expires_in.
(I haven't dealt with refresh token's yet)
Code to validate tokens using the above libraries:
function validateTokenFacebook($token, $id=null) {
// Performed above
// FacebookSession::setDefaultApplication($config->fb->app_id, $config->fb->secret);
$session = new FacebookSession($token);
// Fetch user info
$request = new FacebookRequest($session, 'GET', '/me');
try {
$response = $request->execute();
} catch (\Facebook\FacebookServerException $e) {
$this->mlog->err($e . "\n" . $e->getTraceAsString());
throw new AuthTokenInvalidException();
}
$graphObject = $response->getGraphObject();
$user_id = $graphObject->getProperty('id');
return array(access_token, $user_id);
}
function validateTokenGoogle($token, $id=null) {
$resp=array();
// This key isn't included in the token from hello.js, but
// google needs it
if (!array_key_exists('created', $token)) $token['created'] = $token['expires'] - $token['expires_in'];
$client = new \Google_Client();
$client->setClientId($this->systemConfig->google->app_id);
$client->setClientSecret($this->systemConfig->google->secret);
$client->setRedirectUri($this->systemConfig->google->redirectUri);
$client->setScopes('email');
$client->setAccessToken(json_encode($token));
try {
// Send Client Request
$objOAuthService = new \Google_Service_Oauth2($client);
$userData = $objOAuthService->userinfo->get();
return array($token['access_token'], $userData['id']);
} catch (\Google_Auth_Exception $e) {
throw new AuthException('Google returned ' . get_class($e));
}
}
function validateTokenSoundcloud($token, $id=null) {
$soundcloud = new \Soundcloud\Service(
$this->systemConfig->soundcloud->app_id,
$this->systemConfig->soundcloud->secret,
$this->systemConfig->soundcloud->redirect);
$soundcloud->setAccessToken($access_token);
try {
$response = json_decode($soundcloud->get('me'), true);
if (array_key_exists('id', $response))
return array($access_token, $response['id']);
} catch (Soundcloud\Exception\InvalidHttpResponseCodeException $e) {
$this->mlog->err($e->getMessage());
}
throw new AuthTokenInvalidException();
}
I have a few custom classes above, such as the Exceptions and the systemConfig, but I think it's verbose enough to communicate what they do.

request Google Analytics data from a local server

I want to write a PHP script that imports web stats data from GA. The script is accessible through a web front end (for triggering the import) and resides on a local server (127.0.0.1).
As I understood from the documentation is that there are two options for authenticating and using the core API:
API key - grants only access to statistics
OAuth2 - full authorization
If I understand the mechanics of OAuth2 correctly then this is not an option in my scenario because I cannot specify a callback URL. Hacky solutions come to my mind - like establishing a web profile authentication directly connecting to GA from the browser and then fetching the data by JavaScript and feeding it to the import script - but I would prefer to refrain from such solutions. Also because the browser interaction triggering the import process might be substituted with a cron job in the future.
The API key seems to be exactly what I want but the GET request from the browser fails.
GET request:
https://www.googleapis.com/analytics/v3/data/ga
?ids=ga:[profile ID]
&start-date=2013-01-01&end-date=2013-01-05
&metrics=ga:visits
&key=[the API key]
Response:
{
error: {
errors: [
{
domain: "global",
reason: "required",
message: "Login Required",
locationType: "header",
location: "Authorization"
}
],
code: 401,
message: "Login Required"
}
}
The URL though should be fine. Except for the key parameter it is the same as the one generated with http://ga-dev-tools.appspot.com/explorer/ which is also working (AOuth2 is used in that case). The API key is fresh.
Then again generating a new API key confronts me with the next inconveniency which is that apparently the key is only valid for a day.
So at the end of the day my question is this:
Is it possible to fetch data in the above described scenario without having to authenticate manually or generate API keys on a daily basis?
As already suggested, use this library: https://code.google.com/p/google-api-php-client/
but, instead of using oauth, create a service account from the api console (just select server application). This will provide you with a client id, an email that identify the service account, and *.p12 file holding the private key.
You then have to add the service account (the email) to your analytics as an admin user in order to get the data you need.
To use the service:
$client = new Google_Client();
$client->setApplicationName('test');
$client->setAssertionCredentials(
new Google_AssertionCredentials(
EMAIL,
array('https://www.googleapis.com/auth/analytics.readonly'),
file_get_contents(PRIVATE_KEY_FILEPATH)
)
);
$client->setClientId(CLIENT_ID);
$client->setAccessType('offline_access');
$analytics = new Google_AnalyticsService($client);
To get some data:
$analytics->data_ga->get(PROFILE_ID, $date_from, $date_to, $metrics, $optParams)
For the details check api docs. Also, be careful, there is a query cap (unless you pay)
I think to get this working, you need to use OAuth but with a slight modification to run it from server. Google calls this auth method "Using OAuth 2.0 for Web Server Applications"
As described on that page, you can use a PHP client library to get the authentication done. The client library is located here.
An example example on how to use this client library are on the same project's help pages. Note that you'll have to make some modifications to the code as the comments say to store the token in db and to refresh it regularly.
<?php
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_PlusService.php';
// Set your cached access token. Remember to replace $_SESSION with a
// real database or memcached.
session_start();
$client = new Google_Client();
$client->setApplicationName('Google+ PHP Starter Application');
// Visit https://code.google.com/apis/console?api=plus to generate your
// client id, client secret, and to register your redirect uri.
$client->setClientId('insert_your_oauth2_client_id');
$client->setClientSecret('insert_your_oauth2_client_secret');
$client->setRedirectUri('insert_your_oauth2_redirect_uri');
$client->setDeveloperKey('insert_your_simple_api_key');
$plus = new Google_PlusService($client);
if (isset($_GET['code'])) {
$client->authenticate();
$_SESSION['token'] = $client->getAccessToken();
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
}
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
if ($client->getAccessToken()) {
$activities = $plus->activities->listActivities('me', 'public');
print 'Your Activities: <pre>' . print_r($activities, true) . '</pre>';
// We're not done yet. Remember to update the cached access token.
// Remember to replace $_SESSION with a real database or memcached.
$_SESSION['token'] = $client->getAccessToken();
} else {
$authUrl = $client->createAuthUrl();
print "<a href='$authUrl'>Connect Me!</a>";
}
I have a similar setup. The thing that you don't realize is that you can specify a http://localhost or http://127.0.0.1 or anything else as an origin and callback URL. You need to setup some web interface on your local server that initiates an OAuth setup for the user with the GA access. Note that this is one time. The callback handler must be something like this:
Note: The libraries used here are the same as the previous answer, the detailed code is in the wrapper.
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . '/content/business-intelligence';
if (isset($_GET['code'])) {
require_once 'GAPI.php';
$client = GAPI::init(); //create client instance of Google_Client
$client->authenticate(); //convert auth code to access token
$token = $client->getAccessToken();
$retVal = CF_GAPI::persistToken($token); //save token
if($retVal)
$redirect .= "?new_token";
else
$redirect .= "?bad_token";
}
header('Location: ' . $redirect); //redirect to bi index
Once you have saved the token saved, you must set it in the client before making requests to GA to get your analytics data. Like:
try {
$token = GAPI::readToken(); //read from persistent storage
} catch (Exception $e) {
$token = FALSE;
}
if($token == FALSE) {
$logger->crit("Token not set before running cron!");
echo "Error: Token not set before running cron!";
exit;
}
$client = GAPI::init(); //instance of Google_Client
$client->setAccessToken($token);
The GAPI::init() is implemented as follows:
$client = new Google_Client();
$client->setApplicationName(self::APP_NAME);
$client->setClientId(self::CLIENT_ID);
$client->setClientSecret(self::CLIENT_SECRET);
$client->setRedirectUri(self::REDIRECT_URI);
$client->setDeveloperKey(self::DEVELOPER_KEY);
//to specify that the token is stored offline
$client->setAccessType('offline');
//all results will be objects
$client->setUseObjects(true);
//tell that this app will RO from Analytics
$client->setScopes('https://www.googleapis.com/auth/analytics.readonly');
return $client;
My mysql table has columns like id, title, send_to_emails, frequency, dimensions, metrics, filters, profile_id which completely define each report to the generated from GA. You can play around with them using the documentation, list of metrics & dimensions and the sandbox tester that you already know about.

Generate a Dwolla access token using AJAX instead of redirecting to their webpage?

Looking at Dwolla's API documentation and trying the oauth.php example code (code shown below) on my site it is not clear to me if I can generate an access token without redirecting to Dwolla's page.
Redirecting from my site to their site back to my site is really terrible from a UI/UX perspective and is no better than the crappy interface Paypal provides.
Does anyone know how to generate a Dwolla access token using AJAX?
<?php
// Include the Dwolla REST Client
require '../lib/dwolla.php';
// Include any required keys
require '_keys.php';
// OAuth parameters
$redirectUri = 'http://localhost:8888/oauth.php'; // Point back to this file/URL
$permissions = array("Send", "Transactions", "Balance", "Request", "Contacts", "AccountInfoFull", "Funding");
// Instantiate a new Dwolla REST Client
$Dwolla = new DwollaRestClient($apiKey, $apiSecret, $redirectUri, $permissions);
/**
* STEP 1:
* Create an authentication URL
* that the user will be redirected to
**/
if(!isset($_GET['code']) && !isset($_GET['error'])) {
$authUrl = $Dwolla->getAuthUrl();
header("Location: {$authUrl}");
}
/**
* STEP 2:
* Exchange the temporary code given
* to us in the querystring, for
* a never-expiring OAuth access token
**/
if(isset($_GET['error'])) {
echo "There was an error. Dwolla said: {$_GET['error_description']}";
}
else if(isset($_GET['code'])) {
$code = $_GET['code'];
$token = $Dwolla->requestToken($code);
if(!$token) { $Dwolla->getError(); } // Check for errors
else {
session_start();
$_SESSION['token'] = $token;
echo "Your access token is: {$token}";
} // Print the access token
}
TL;DR - No, that's not how OAuth works
The whole point of the OAuth scheme is authentication on the website of the service that you want to use, in this case, Dwolla. By forcing the user to go to their page it ensures a few things:
The user is made aware that they are using an external service whose terms of service may be different than your application
The user is made aware of the features requested by your application for that service. In dwolla's case there are different levels of functionality that can be requested by your application including transferring of money, so it's important that your users are aware of that!
You can read up more on OAuth at http://oauth.net/

Persistent Google OpenID+OAuth?

I'm working on a web app that will require somewhat frequent access to Google Data APIs, so I decided to go with the "OAuth with Federated Login (Hybrid Protocol)" method for users to log into the app. I got the http://googlecodesamples.com/hybrid/ working (after some tweaks for PHP 5.3 compatibility), and am able to get an Access Token. What's the next step? How do I use this access token?
It seems like I'll need to create a local session for the user to browse the rest of the app. Will this need to be completely independent of the Google login, or how would you handle that?
Relevant: this application also needs a REST API, for which I was planning to use OAuth. Any recommendation on how to tie this in with authentication for the actual app?
I am using the PHP LightOpenID library (see on gitorious) for that. It handles all the authentication flow for us. You don't need to bother about token and stuff.
Here the page where I display the "Login with Google" link :
<?php
require_once 'openid.php';
$openid = new LightOpenID;
$openid->identity = 'https://www.google.com/accounts/o8/id';
$openid->required = array('contact/email');
$openid->returnUrl = 'http://my-website.com/landing-login.php'
?>
Login with Google
When the click on the link, a Google page will appear ask him to authenticate and/or authorize you to retrieve his email.
Then he will be redirect to the landing page $openid->returnUrl. The code for that page should be :
<?php
require_once 'openid.php';
$openid = new LightOpenID;
if ($openid->mode) {
if ($openid->mode == 'cancel') {
// User has canceled authentication
} elseif($openid->validate()) {
// Yeah !
$data = $openid->getAttributes();
$email = $data['contact/email'];
} else {
// The user has not logged in via Google
}
} else {
// The user does not come from the link of the first page
}
?>
If you want to retrieve more info from the user, you have to add them to $openid->required in the first page. For instance :
$openid->required = array(
'contact/email',
'namePerson/first',
'namePerson/last'
);
will let you, if the user accepts it, to get his first and last names as well in the second page :
$name = $data['namePerson/first'] . " " . $data['namePerson/last'];
Then, for the Oauth part, you can follow the instructions of this LightOpenID issue.

Categories