I am trying to use the Gmail API to access the email in a web application. I have tried the example at https://developers.google.com/gmail/api/quickstart/php and is working fine. Now I want to get the access token using javascript API and use in the above example.
gapi.auth2.authorize({
client_id: 'CLIENT_ID.apps.googleusercontent.com',
scope: 'email profile openid',
response_type: 'id_token permission'
}, function(response) {
if (response.error) {
// An error happened!
return;
}
// The user authorized the application for the scopes requested.
var accessToken = response.access_token;
var idToken = response.id_token;
// You can also now use gapi.client to perform authenticated requests.
});
When I manually add the response from javascript API in the php script it is showing error
Uncaught exception 'LogicException' with message 'refresh token must be passed in or set as part of setAccessToken' in D:\wamp\www\gmailexample\google-api-php-client-2.2.0\src\Google\Client.php:267
Below is the php script I am using.
<?php
require_once '/google-api-php-client-2.2.0/vendor/autoload.php';
define('APPLICATION_NAME', 'Gmail API PHP Quickstart');
define('CREDENTIALS_PATH', '~/.credentials/gmail-php-quickstart.json');
define('CLIENT_SECRET_PATH', 'client_secret.json');
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-php-quickstart.json
define('SCOPES', implode(' ', array(
Google_Service_Gmail::GMAIL_READONLY)
));
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(APPLICATION_NAME);
$client->setScopes(SCOPES);
$client->setAuthConfig(CLIENT_SECRET_PATH);
$client->setAccessType('offline');
// Load previously authorized credentials from a file.
$credentialsPath = expandHomeDirectory(CREDENTIALS_PATH);
$accessToken= json_decode('{"access_token":"asdfsasdfsasdfsasdfs","expires_in":2255,"expires_at":254877}', true);
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
return $client;
}
/**
* Expands the home directory alias '~' to the full path.
* #param string $path the path to expand.
* #return string the expanded path.
*/
function expandHomeDirectory($path) {
$homeDirectory = getenv('HOME');
if (empty($homeDirectory)) {
$homeDirectory = getenv('HOMEDRIVE') . getenv('HOMEPATH');
}
return str_replace('~', realpath($homeDirectory), $path);
}
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Gmail($client);
// Print the labels in the user's account.
$user = 'me';
$results = $service->users_labels->listUsersLabels($user);
if (count($results->getLabels()) == 0) {
print "No labels found.\n";
} else {
print "Labels:\n";
foreach ($results->getLabels() as $label) {
printf("- %s\n", $label->getName());
}
}
Please help.
Ok my friend, here is your solution. To make sure you understand this, I worked an example. First, inside your working directory, make sure you have the Google PHP Client library and two other files. The first one should be called index.php and paste the following code inside that file:
<html>
<head>
<title>JS/PHP Google Sample</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script type="text/javascript">
function handleClientLoad() {
// Loads the client library and the auth2 library together for efficiency.
// Loading the auth2 library is optional here since `gapi.client.init` function will load
// it if not already loaded. Loading it upfront can save one network request.
gapi.load('client:auth2', initClient);
}
function initClient() {
// Initialize the client with API key and People API, and initialize OAuth with an
// OAuth 2.0 client ID and scopes (space delimited string) to request access.
gapi.client.init({
apiKey: 'YOUR API KEY GOES HERE',
clientId: 'YOUR CLIENT ID GOES HERE',
scope: 'email profile https://www.googleapis.com/auth/gmail.readonly',
}).then(function () {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
});
}
function updateSigninStatus(isSignedIn) {
// When signin status changes, this function is called.
// If the signin status is changed to signedIn, we make an API call.
if (isSignedIn) {
$("#signout-button").show();
$("#signin-button").hide();
makeApiCall();
} else {
$("#signin-button").show();
$("#signout-button").hide();
}
}
function handleSignInClick(event) {
// Ideally the button should only show up after gapi.client.init finishes, so that this
// handler won't be called before OAuth is initialized.
gapi.auth2.getAuthInstance().signIn();
}
function handleSignOutClick(event) {
var host = "http://"+window.location.hostname;
gapi.auth2.GoogleUser.prototype.disconnect();
window.open(host, "_self");
}
function makeApiCall() {
// Make an API call to the People API, and print the user's given name.
var accsTkn = gapi.auth2.getAuthInstance().$K.Q7.access_token;
var formData = new FormData();
formData.append("access_token", accsTkn); //send access token
$.ajax({
url : 'listEmails.php',
type : 'POST',
data : formData,
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
success : function(html) {
$("#myLabels").append(html);
}
});
}
</script>
<style>
#signin-button{
display: none;
}
#signout-button{
display: none;
}
#myLabels{
width: 80%;
min-height: 350px;
}
</style>
</head>
<body>
<center>
<h1>Google OAuth Gmail Example with Javascript and PHP</h1><br>
<button id="signin-button" onclick="handleSignInClick()">Sign In</button>
<br><br><br>
<div id="myLabels">
Emails list: <br><br>
</div>
<br><br>
<button id="signout-button" onclick="handleSignOutClick()">Sign Out</button>
</center>
<script async defer src="https://apis.google.com/js/api.js" onload="this.onload=function(){};handleClientLoad()" onreadystatechange="if (this.readyState === 'complete') this.onload()"> </script>
</body>
</html>
Next, the second file should be named listEmails.php and paste the following code inside:
<?php session_start();
require_once "path_to_php_client_lib/vendor/autoload.php"; //include php client library
//set the required parameteres
$scopes = array("https://www.googleapis.com/auth/gmail.readonly");
$client = new Google_Client();
$client->setRedirectUri('http://'.$_SERVER['HTTP_HOST'].'listEmails.php');
$client->setAuthConfig("client_secret.json");
$client->addScope($scopes);
$client->setAccessToken($_POST["access_token"]);
$service = new Google_Service_Gmail($client); // define service to be rquested
$pageToken = NULL;
$messages = array();
$opt_param = array("maxResults" => 5);
try {
$messagesResponse = $service->users_messages->listUsersMessages("me", $opt_param);
if ($messagesResponse->getMessages()) {
$messages = array_merge($messages, $messagesResponse->getMessages());
}
} catch (Exception $e) {
print 'An error occurred: ' . $e->getMessage();
}
foreach ($messages as $message) {
$msgId = $message->getId();
$optParams = array("format" => "full");
$uniqueMsg = $service->users_messages->get("me", $msgId, $optParams);
print 'Message with ID: ' . $uniqueMsg->id . '<br>';
print 'Message From: ' . $uniqueMsg->getPayload()->getHeaders()[18]->value . '<br><br>**************************<br><br>';
}
?>
Understanding the example:
The documentation here clearly explains
This OAuth 2.0 flow is called the implicit grant flow. It is designed for applications that access APIs only while the user is present at the application. These applications are not able to store confidential information.
That means that using the Javascript authentication flow you will NOT be able to get offline access. Having that in mind, we can move on.
As you can then see on the php script, you are not required to refresh the token since this will be managed by the Javascript client library; And there you go... I hope this helps!
Related
I would like to access a public google drive folder and get all the images from it. This works, but I would like to be able to enter without having to login in a popup. I read that you can actually do this by using the .json file from a service account. But I haven't seen any code for this approach. Where and what should I change in this code? (I'm including the whole code because I don't know exactly where to initiate this service account authentication.)
<html>
<head>
<title>My Google Drive Images</title>
</head>
<body>
<h1>My Google Drive Images</h1>
<div id="image-container"></div>
<!--Add buttons to initiate auth sequence and sign out-->
<button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
<button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>
<pre id="content" style="white-space: pre-wrap;"></pre>
<script type="text/javascript">
/* exported gapiLoaded */
/* exported gisLoaded */
/* exported handleAuthClick */
/* exported handleSignoutClick */
// TODO(developer): Set to client ID and API key from the Developer Console
const CLIENT_ID = 'CLIENT_ID';
const API_KEY = 'API_KEY';
// Discovery doc URL for APIs used by the quickstart
const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
const SCOPES = 'https://www.googleapis.com/auth/drive';
let tokenClient;
let gapiInited = false;
let gisInited = false;
document.getElementById('authorize_button').style.visibility = 'hidden';
document.getElementById('signout_button').style.visibility = 'hidden';
/**
* Callback after api.js is loaded.
*/
function gapiLoaded() {
gapi.load('client', initializeGapiClient);
}
/**
* Callback after the API client is loaded. Loads the
* discovery doc to initialize the API.
*/
async function initializeGapiClient() {
await gapi.client.init({
apiKey: API_KEY,
discoveryDocs: [DISCOVERY_DOC],
});
gapiInited = true;
maybeEnableButtons();
}
/**
* Callback after Google Identity Services are loaded.
*/
function gisLoaded() {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: CLIENT_ID,
scope: SCOPES,
callback: '', // defined later
});
gisInited = true;
maybeEnableButtons();
}
/**
* Enables user interaction after all libraries are loaded.
*/
function maybeEnableButtons() {
if (gapiInited && gisInited) {
document.getElementById('authorize_button').style.visibility = 'visible';
}
}
/**
* Sign in the user upon button click.
*/
function handleAuthClick() {
tokenClient.callback = async (resp) => {
if (resp.error !== undefined) {
throw (resp);
}
document.getElementById('signout_button').style.visibility = 'visible';
document.getElementById('authorize_button').innerText = 'Refresh';
await listFiles();
};
if (gapi.client.getToken() === null) {
// Prompt the user to select a Google Account and ask for consent to share their data
// when establishing a new session.
tokenClient.requestAccessToken({prompt: 'consent'});
} else {
// Skip display of account chooser and consent dialog for an existing session.
tokenClient.requestAccessToken({prompt: ''});
}
}
/**
* Sign out the user upon button click.
*/
function handleSignoutClick() {
const token = gapi.client.getToken();
if (token !== null) {
google.accounts.oauth2.revoke(token.access_token);
gapi.client.setToken('');
document.getElementById('content').innerText = '';
document.getElementById('authorize_button').innerText = 'Authorize';
document.getElementById('signout_button').style.visibility = 'hidden';
}
}
function listFiles() {
console.log("Listing files in folder");
gapi.client.drive.files.list({
"includeItemsFromAllDrives": true,
"supportsAllDrives": true,
"q": "trashed = false and parents in 'FOLDER_ID'"
}).then(function(response) {
console.log("Files listed successfully");
var files = response.result.files;
if (files && files.length > 0) {
console.log(files.length + " files found");
for (var i = 0; i < files.length; i++) {
var file = files[i];
var fileId = file.id;
var fileUrl = "https://drive.google.com/u/0/uc?id=" + fileId;
var imgTag = document.createElement("img");
imgTag.src = fileUrl;
document.getElementById("image-container").appendChild(imgTag);
}
} else {
console.log("No files found.");
}
}, function(reason) {
console.log("Error listing files: " + reason.result.error.message);
});
}
</script>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
</body>
</html>
I already tried changing the scopes and I read all that I could, but this topic is just not on the internet. The best case would be to don't use OAuth, but as I've read it's needed for accessing this API. Even the Google Drive API Reference only says this note, but it doesn't elaborate on this aspect:
Note: Authorization optional.
Could somebody please help me on this topic?
I would like to access a public google drive folder and get all the images from it.
If all you want to do is read it and its public, then you could just use an API Key.
I read that you can actually do this by using the .json file from a service account.
Client side JavaScript does not support service account authorization flow. You need to use implicit flow in and request authorization of a user to access the ir private data.
To use Service accounts you must use a server sided programing language
I am very new to using API and getting JSON data using OAuth. Could anybody help me? I am trying to access clients google photos and read them. These code snippets are from google photos documentation. I modified it but still having error: "Failed to load resource: the server responded with a status of 401 ()" and "Uncaught {error: "idpiframe_initialization_failed", details: "Not a valid origin for the client: http://127.0.0.…itelist this origin for your project's client ID."}"
Thank you!!!
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script async defer src="https://apis.google.com/js/api.js"
onload="this.onload=function(){};handleClientLoad()"
onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
<script>
var GoogleAuth;
var SCOPE = 'https://www.googleapis.com/auth/drive.photos.readonly';
function handleClientLoad() {
// Load the API's client and auth2 modules.
// Call the initClient function after the modules load.
gapi.load('client:auth2', initClient);
}
function initClient() {
// Retrieve the discovery document for version 3 of Google Drive API.
// In practice, your app can retrieve one or more discovery documents.
var discoveryUrl = 'https://www.googleapis.com/discovery/v1/apis/photos/v1/rest';
// Initialize the gapi.client object, which app uses to make API requests.
// Get API key and client ID from API Console.
// 'scope' field specifies space-delimited list of access scopes.
gapi.client.init({
'apiKey': 'XXXXXXXXXXXX',
'discoveryDocs': [discoveryUrl],
'clientId': 'XXXXXXXXXXXXXXXXXX',
'scope': SCOPE
}).then(function () {
GoogleAuth = gapi.auth2.getAuthInstance();
// Listen for sign-in state changes.
GoogleAuth.isSignedIn.listen(updateSigninStatus);
// Handle initial sign-in state. (Determine if user is already signed in.)
var user = GoogleAuth.currentUser.get();
setSigninStatus();
// Call handleAuthClick function when user clicks on
// "Sign In/Authorize" button.
$('#sign-in-or-out-button').click(function () {
handleAuthClick();
});
$('#revoke-access-button').click(function () {
revokeAccess();
});
});
}
function handleAuthClick() {
if (GoogleAuth.isSignedIn.get()) {
// User is authorized and has clicked 'Sign out' button.
GoogleAuth.signOut();
} else {
// User is not signed in. Start Google auth flow.
GoogleAuth.signIn();
}
}
function revokeAccess() {
GoogleAuth.disconnect();
}
function setSigninStatus(isSignedIn) {
var user = GoogleAuth.currentUser.get();
var isAuthorized = user.hasGrantedScopes(SCOPE);
if (isAuthorized) {
$('#sign-in-or-out-button').html('Sign out');
$('#revoke-access-button').css('display', 'inline-block');
$('#auth-status').html('You are currently signed in and have granted ' +
'access to this app.');
} else {
$('#sign-in-or-out-button').html('Sign In/Authorize');
$('#revoke-access-button').css('display', 'none');
$('#auth-status').html('You have not authorized this app or you are ' +
'signed out.');
}
}
function updateSigninStatus(isSignedIn) {
setSigninStatus();
}
</script>
<button id="sign-in-or-out-button"
style="margin-left: 25px">Sign In/Authorize
</button>
<button id="revoke-access-button"
style="display: none; margin-left: 25px">Revoke access
</button>
<div id="auth-status" style="display: inline; padding-left: 25px"></div>
use this link to get more details
On right side there is button Execute, on click that button you will get all photos ,
you can also find code just clicking a icon right side square icon of text Try this API, a popup will open, click on JAVASCRIPT Tab , you will find code
https://developers.google.com/photos/library/reference/rest/v1/mediaItems/search
Accessing Google Photo API with your standard Google Apps Script token
I believe you can use the token that you already have with Google Apps Script.
I did go into the Console and setup the credentials for this project but I'm not using them.
function listImages() {
var token='';
var html='';
var n=0;
do{
var params = {muteHttpExceptions:true,headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()}};
var url=Utilities.formatString('https://photoslibrary.googleapis.com/v1/mediaItems?pageSize=100%s',(token!=null)?"&pageToken=" + token:"");
var resp=UrlFetchApp.fetch(url,params);
Logger.log(resp);
var js=JSON.parse(resp.getContentText());
for(var i=0;i<js.mediaItems.length;i++) {
html+=Utilities.formatString('<br />%s - File Name: %s<br /><img src="%s" width="265"/>',++n,js.mediaItems[i].filename,js.mediaItems[i].baseUrl);
}
token=js.nextPageToken;
}while(token!=null);
var userInterface=HtmlService.createHtmlOutput(html).setWidth(1200).setHeight(500);
//SpreadsheetApp.getUi().showModelessDialog(userInterface, 'Images')//dialog
SpreadsheetApp.getUi().showSidebar(userInterface);//sidebard
}
Try This Code
call onAuthPhotoApiLoad function on button click
**also include js of google **
var scopeApi = ['https://www.googleapis.com/auth/photoslibrary', 'https://www.googleapis.com/auth/photoslibrary.readonly', 'https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata'];
function onAuthPhotoApiLoad() {
window.gapi.auth.authorize(
{
'client_id': "Put Client ID Here",
'scope': scopeApi,
'immediate': false
},
handlePhotoApiAuthResult);
}
function handlePhotoApiAuthResult(authResult) {
if (authResult && !authResult.error) {
oauthToken = authResult.access_token;
GetAllPhotoGoogleApi();
}
}
function GetAllPhotoGoogleApi() {
gapi.client.request({
'path': 'https://photoslibrary.googleapis.com/v1/mediaItems:search',
'method': 'POST',
'body': {
"filters": {
"mediaTypeFilter": {
"mediaTypes": ["PHOTO"]
}
}
}
}).then(function (response) {
console.log(response);
}, function (reason) {
console.log(reason);
});
}
I'm looking to send out an email (specifically the values entered into a form) from a specific gmail account, and due to platform limitations I have to use client side (i.e. javascript) code to do so. I'm testing the following code, but it seems to be using my personal gmail credentials, rather than the client id I'm trying to specify. What am I doing wrong?
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Base64/1.0.0/base64.min.js"></script>
<script type="text/javascript">
var CLIENT_ID = 'myclientid.apps.googleusercontent.com';
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"];
var SCOPES = 'https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.send';
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
function initClient() {
gapi.client.init({
discoveryDocs: DISCOVERY_DOCS,
clientId: CLIENT_ID,
scope: SCOPES
});
}
// ucs-2 string to base64 encoded ascii
function utoa(str) {
return window.btoa(unescape(encodeURIComponent(str)));
}
// base64 encoded ascii to ucs-2 string
function atou(str) {
return decodeURIComponent(escape(window.atob(str)));
}
function submitForm(form) {
var formid = form.id.length>0 ? '('+form.id+') ' : '';
var bodyText='The form '+formid+'was filled out on '+window.location.href+'\r\n'+
'The following values were submitted:';
for (var i=0;i<form.childElementCount;i++) {
bodyText += '\r\n'+'\r\n'+form[i].name+'\r\n'+form[i].value;
}
var unencodedEmail = 'To: '+form.attributes.getNamedItem('data-submit-to').value+
'\r\nSubject: A form '+formid+'was filled out on '+window.location.href+
'\r\n'+bodyText;
try {
var authInstance = gapi.auth2.getAuthInstance();
authInstance.signIn({ client_id: CLIENT_ID });
try {
var base64EncodedEmail = utoa(unencodedEmail);
var request = gapi.client.gmail.users.messages.send({
'userId': 'me',
'resource': {
'message': {
'raw': base64EncodedEmail
}
}
});
request.execute(function() {/*do post email sending stuff*/});
} finally {
authInstance.signOut();
}
} finally {
return false;//prevent the form from redirecting
}
}
</script>
<script async defer src="https://apis.google.com/js/api.js"
onload="this.onload=function(){};handleClientLoad()"
onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
You cannot use other people's credentials, you need to use your own. If you follow the Javascript Quickstart for Gmail, you'll find out that the credentials will be from your Google mail account. This is the reason we have a thing called OAuth. If Google allowed that, this would be open to exploitation and malign use.
I would like to display Google Analytics data on my website. I have loaded the php libraries with my script and now it returns "Total sessions: 94" (which is actually the correct response). The quick-and-easy sample shows just a few lines of code to display formatted widgets with charts & graphs, but it is JAVASCRIPT NOT PHP.
The PHP part is now authenticating and returning the correct information but it does not display nicely.
How do you get the API to display formatted charts and graphs on a
website?
Is there a way to just return a big array of data then parse it?
<?php
//-------- Embed API Dashboard with Server-Side Authorization
//-------- https://ga-dev-tools.appspot.com/embed-api/basic-dashboard/
/*
//-------- Load the Google API PHP Client Library.
require_once '/src/Google/autoload.php';
//-------- Set variables
$client_id = 'BIG-LONG-SERVER-API-KEY';
$email_address = 'SERVER-API-KEY#ACCOUNT.gserviceaccount.com';
$key_file_location = 'client_secrets.p12';
$key = file_get_contents($key_file_location);
*/
//-------- Start a session to persist credentials
session_start();
//-------- Create the Google client
$client = new Google_Client();
$client->setApplicationName('GA Analytics API Sample');
//-------- Separate additional scopes with a comma
$scopes ="https://www.googleapis.com/auth/analytics.readonly";
//-------- Server Side Auth
$cred = new Google_Auth_AssertionCredentials(
$email_address,
array($scopes),
$key
);
$client->setAssertionCredentials($cred);
if($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion($cred);
}
$client->setClientId($client_id);
// Create an authorized analytics service object.
$analytics = new Google_Service_Analytics($client);
// Get the first view (profile) id for the authorized user.
$profile = getFirstProfileId($analytics);
?>
<!DOCTYPE html>
<html>
<head>
<title>Embed API Demo</title>
</head>
<body>
<?php
//-------- Get the results from the Core Reporting API and print the results
$results = getAnalyticsData($analytics, $profile);
// Parses the response from the Core Reporting API and prints
// the profile name and total sessions.
if (count($results->getRows()) > 0)
{
// Get the profile name.
$profileName = $results->getProfileInfo()->getProfileName();
// Get the entry for the first entry in the first row.
$rows = $results->getRows();
$sessions = $rows[0][0];
// Print the results.
print "<p>First view (profile) found: $profileName</p>";
print "<p>Total sessions: $sessions</p>";
} else {
print "<p>No results found.</p>";
}
?>
<!-- Step 1: Create the containing elements. -->
<section id="auth-button"></section>
<section id="view-selector"></section>
<section id="timeline"></section>
<!-- Step 2: Load the library. -->
<script>
(function(w,d,s,g,js,fjs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(cb){this.q.push(cb)}};
js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fjs.parentNode.insertBefore(js,fjs);js.onload=function(){g.load('analytics')};
}(window,document,'script'));
</script>
<script>
gapi.analytics.ready(function() {
// Step 3: Authorize the user.
// This is for "OAuth Client" NOT Server-Side Authorization
var CLIENT_ID = 'BIG-LONG-GOOGLE-API-KEY';
gapi.analytics.auth.authorize({
container: 'auth-button',
clientid: CLIENT_ID,
});
// Step 4: Create the view selector.
var viewSelector = new gapi.analytics.ViewSelector({
container: 'view-selector'
});
// Step 5: Create the timeline chart.
var timeline = new gapi.analytics.googleCharts.DataChart({
reportType: 'ga',
query: {
'dimensions': 'ga:date',
'metrics': 'ga:sessions',
'start-date': '30daysAgo',
'end-date': 'yesterday',
},
chart: {
type: 'LINE',
container: 'timeline'
}
});
// Step 6: Hook up the components to work together.
gapi.analytics.auth.on('success', function(response) {
viewSelector.execute();
});
viewSelector.on('change', function(ids) {
var newIds = {
query: {
ids: ids
}
}
timeline.set(newIds).execute();
});
});
</script>
</body>
</html>
<?php
/*
//----- Built-in Methods?
// Visits by date
$visits = $analytics->getVisitsByDate();
// Get visits by Operating Systems and return max 100 results
$visitsByOs = $analytics->getVisitsBySystemOs(array('max-results' => 100));
// Get referral traffic
$referralTraffic = $analytics->getReferralTraffic();
// Get visits by languages
$visitsByLanguages = $analytics->getVisitsByLanguages();
*/
//----------------------------------------------------//
// Function - Query the "public Google_Service_Analytics_GaData" API
// https://developers.google.com/analytics/devguides/reporting/core/v3/coreDevguide
//----------------------------------------------------//
function getAnalyticsData($analytics, $profileId)
{
$tableid = 'ga:'.$profileId;
$startDate = '7daysAgo';
$endDate = 'today';
$metrics = 'ga:sessions';
// Define optional parameters (dimensions, sort, filters, max-results, )
$optParams = array(
//'dimensions' => 'ga:source,ga:keyword',
'sort' => '-ga:sessions',
'filters' => 'ga:medium==organic',
'max-results' => '25'
);
// Calls the "Core Reporting API" with query
$service = $analytics->data_ga->get(
$tableid,
$startDate,
$endDate,
$metrics,
$optParams
);
return $service;
/*
//-------- Query Submitted to GA
$analytics->data_ga->get(
'ga:PROFILEID',
'7daysAgo',
'today',
'ga:sessions',
'ga:source,ga:keyword',
'-ga:sessions,ga:source',
'ga:medium==organic',
'25'
);
*/
}
//----------------------------------------------------//
// Function - Return first authorized view
//----------------------------------------------------//
function getFirstProfileId($analytics)
{
// Get the list of accounts for the authorized user
$accounts = $analytics->management_accounts->listManagementAccounts();
// Get the user's first view (profile) ID
if (count($accounts->getItems()) > 0)
{
$items = $accounts->getItems();
$firstAccountId = $items[0]->getId();
// Get the list of properties for the authorized user
$properties = $analytics->management_webproperties
->listManagementWebproperties($firstAccountId);
if (count($properties->getItems()) > 0)
{
$items = $properties->getItems();
$firstPropertyId = $items[0]->getId();
// Get the list of views (profiles) for the authorized user
$profiles = $analytics->management_profiles
->listManagementProfiles($firstAccountId, $firstPropertyId);
if (count($profiles->getItems()) > 0)
{
$items = $profiles->getItems();
// Return the first view (profile) ID
return $items[0]->getId();
} else {
throw new Exception('No views (profiles) found for this user');
}
} else {
throw new Exception('No properties found for this user');
}
} else {
throw new Exception('No accounts found for this user');
}
}
//----------------------------------------------------//
// END
//----------------------------------------------------//
?>
Reference Urls:
<?php
"DataChart component of the Embed API queries the Core Reporting API"
https://ga-dev-tools.appspot.com/embed-api/server-side-authorization/
"Embed API Dashboard with Server-Side Authorization"
https://ga-dev-tools.appspot.com/embed-api/basic-dashboard/
"Google_Service_Analytics_GaData API"
https://developers.google.com/analytics/devguides/reporting/core/v3/coreDevguide
"Core Reporting API reference guide"
https://developers.google.com/analytics/devguides/reporting/core/v3/reference
"Dimensions and metrics reference page"
https://developers.google.com/analytics/devguides/reporting/core/dimsmets
?>
I'm attempting a slight variation of the Google+ web sign-in server side flow as described on the Google Developer's website.
I have a staff login page (staff_login.php) which uses javascript via Google+ (plusone.js).
If the user is already signed in to Google then the authorization code from Google is stored to a session variable.
If the user is not signed in then a 'Staff Login' button is displayed. If the user clicks the button then Google authorization takes place and, if successful, then the authorization code from Google is stored to a session variable.
In both cases, after the session variable has been stored, the user is redirected to another web page (google_login.php).
Most of the time the login process works as expected, but sometimes google_login.php generates an error message: Google_Auth_Exception' with message 'Error fetching OAuth2 access token, message: 'invalid_grant'.
I'm fairly sure the problem lies in the signInCallback function. How do I make it bulletproof?
Here's the (cut-down) code:
staff_login.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="google-signin-clientid"
content="CLIENT-ID.apps.googleusercontent.com">
<meta name="google-signin-scope" content="email">
<meta name="google-signin-cookiepolicy" content="single_host_origin">
<meta name="google-signin-callback" content="signInCallback">
<title>Login</title>
</head>
<body>
<button id="xyzStaffSignIn">Staff Sign In</button>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"
type="text/javascript"></script>
<script type = "text/javascript" >
jQuery(document).ready(function ($) {
console.log('Google (plusone.js) will invoke signInCallback');
window.___gcfg = {
lang: 'en-GB',
parsetags: 'onload'
};
var po = document.createElement('script');
po.type = 'text/javascript';
po.async = true;
po.src = 'https://apis.google.com/js/client:plusone.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(po, s);
});
function signInCallback(authResult) {
if (authResult) {
if (authResult['error'] == undefined) {
if (authResult['code']) {
setSessionValue('GoogleAuthorisationCode',
authResult['code'], callGoogleLogin);
}
} else if (authResult['error']) {
// There was an error.
// Possible error codes:
// "access_denied" - User denied access to your app
// "immediate_failed" - Could not automatically log in the user
console.log('There was an error: ' + authResult['error']);
if (!authResult['status']['signed_in']) {
console.log('gapi.signin.render will invoke signInCallback');
gapi.signin.render('xyzStaffSignIn');
}
} else {
console.log('Empty authResult'); // Something went wrong
}
}
}
function setSessionValue(key, value, callback) {
$.post(
'session.php',
{
xyzAction: 'set',
xyzKey: key,
xyzValue: value
},
function (result) {
// Handle or verify the server response if necessary.
if (result['status'] == undefined) {
alert('xyz status problem. Please email our IT department!');
} else {
switch (result['status']) {
case 'Success':
callback();
break;
default:
alert('xyz unexpected status problem.
Please email our IT department!');
console.log(result['status']);
}
}
}
)
}
function callGoogleLogin() {
gapi.client.load('plus', 'v1', loadProfile);
}
/**
* Uses the JavaScript API to request the user's profile, which includes
* their basic information. When the plus.profile.emails.read scope is
* requested, the response will also include the user's primary email address
* and any other email addresses that the user made public.
*/
function loadProfile() {
var request = gapi.client.plus.people.get({'userId': 'me'});
request.execute(loadProfileCallback);
}
/**
* Callback for the asynchronous request to the people.get method. The profile
* and email are set to global variables. Triggers the user's basic profile
* to display when called.
*/
function loadProfileCallback(profile) {
var emailAddress;
// Filter the emails object to find the user's primary account, which might
// not always be the first in the array. The filter() method supports IE9+.
emailAddress = profile['emails'].filter(function (v) {
return v.type === 'account'; // Filter out the primary email
})[0].value; // get the email from the filtered results, should always be defined.
var domain = emailAddress.replace(/.*#/, "");
if ("xyz.com" == domain) {
window.location.href = "google_login.php?xyzEmailAddress=" + emailAddress;
} else {
alert(emailAddress + ' is not a recognized xyz staff member email address.');
}
}
</script>
</body>
</html>
google_login.php
<?php
// This code is called from the javascript on the login screen only
// AFTER Google authorization has succeeded
// Google_Client is as defined at
// https://github.com/google/google-api-php-client/blob/master/src/Google/Client.php
$googleClient = new Google_Client ();
$googleClient->setRedirectUri('postmessage');
$googleClient->authenticate($_SESSION['GoogleAuthorizationCode']);
Need to add/enable APIs from the left hand side panel here https://console.developers.google.com.
APIs which I have added are "google+ API" and "gmail API". I tried and it worked for me.