I read the information and examples from the Sign in with Google Guides and the Google Identity Services JavaScript SDK Guides and I managed to set up authorization using Tokens to Google Cloud APIs. Unfortunately, I still get a login prompt to select my Google Account every time I load the page even after the user consent has been given on first login.
I saw that requestAccessToken allows specifying a user email as hint via OverridableTokenClientConfig for the login, but even if I specify the hint I still get the login prompt every time requestAccessToken is running.
Ultimately, I want to use Sign in with Google to allow the user to automatically sign in and provide user information like email to the app. Then requestAccessToken should use that email to request the token without prompting the user again (except for the first time where the user needs to give consent to the devstorage.full_control scope).
This is the current code I'm using (to run it, you need to insert a valid clientId, bucket and object):
<html>
<head>
<!-- Google Identity Service see https://developers.google.com/identity/oauth2/web/guides/migration-to-gis#gis-and-gapi -->
<script src="https://accounts.google.com/gsi/client" onload="initTokenClient()" async defer></script>
<script>
var tokenClient;
var access_token;
var clientId = '123456789012-abcdefghijklmnopqrstuvxyz123456.apps.googleusercontent.com';
var email = 'florian.feldhaus#gmail.com'
var bucket = 'private-bucket'
var object = 'private-object.json'
function initTokenClient() {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: clientId,
scope: 'https://www.googleapis.com/auth/devstorage.full_control',
callback: (tokenResponse) => {
access_token = tokenResponse.access_token;
},
});
}
function getToken() {
// Re-entrant function to request user consent.
// Returns an access token to the callback specified in google.accounts.oauth2.initTokenClient
// Use a user gesture to call this function and obtain a new, valid access token
// when the previous token expires and a 401 status code is returned by Google API calls.
tokenClient.requestAccessToken({
hint: email
}
);
}
function getObject() {
fetch('https://storage.googleapis.com/storage/v1/b/' + bucket + '/o/' + object + '?alt=media', {
headers: {
'Authorization': 'Bearer ' + access_token
}
})
.then(response => response.json())
.then(content => console.log(content))
.catch(err => { console.log(err) });
}
</script>
</head>
<body>
<button onclick="getToken();">Get access token</button>
<button onclick="getObject()">Load Object</button>
</body>
</html>
Getting the access token always opens a login prompt like this
The login prompt can be prevented by setting an empty prompt parameter in initTokenClient. This will only prompt for the login and user consent on the first login. All further logins will use the selected account from the first login. This still briefly shows a popup with a spinning wheel which usually is gone within a second.
If someone has more information to share on how to get rid of the popup and integrate with Sign in with Google please share as a separate answer.
My full example now looks like this (removing hint and adding prompt):
<html>
<head>
<!-- Google Identity Service see https://developers.google.com/identity/oauth2/web/guides/migration-to-gis#gis-and-gapi -->
<script src="https://accounts.google.com/gsi/client" onload="initTokenClient()" async defer></script>
<script>
var tokenClient;
var access_token;
var clientId = '123456789012-abcdefghijklmnopqrstuvxyz123456.apps.googleusercontent.com';
var email = 'florian.feldhaus#gmail.com'
var bucket = 'private-bucket'
var object = 'private-object.json'
function initTokenClient() {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: clientId,
scope: 'https://www.googleapis.com/auth/devstorage.full_control',
prompt: '',
callback: (tokenResponse) => {
access_token = tokenResponse.access_token;
},
});
}
function getToken() {
// Re-entrant function to request user consent.
// Returns an access token to the callback specified in google.accounts.oauth2.initTokenClient
// Use a user gesture to call this function and obtain a new, valid access token
// when the previous token expires and a 401 status code is returned by Google API calls.
tokenClient.requestAccessToken();
}
function getObject() {
fetch('https://storage.googleapis.com/storage/v1/b/' + bucket + '/o/' + object + '?alt=media', {
headers: {
'Authorization': 'Bearer ' + access_token
}
})
.then(response => response.json())
.then(content => console.log(content))
.catch(err => { console.log(err) });
}
</script>
</head>
<body>
<button onclick="getToken();">Get access token</button>
<button onclick="getObject()">Load Object</button>
</body>
</html>
Related
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 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!
I am implementing Google log in for the first time as described here and here.
I am using HTML with Javascript.
The problem that needs solving is as follows: How can I, after the initial login, on a different page (say a landing page, or portal that the user sees after logging in), check if the user is logged in? Is there a service I can call to check the user's login in status with my app key or something similar?
I assume I would have to include the google API on each page.
Login Page Code:
Script In Head (Code from Google's tutorial listed above):
<head>
....
<script src="https://apis.google.com/js/platform.js" async defer></script>
<script>
function onSignIn(googleUser)
{
var profile = googleUser.getBasicProfile();
console.log('ID: ' + profile.getId());
console.log('Name: ' + profile.getName());
console.log('Image URL: ' + profile.getImageUrl());
console.log('Email: ' + profile.getEmail());
alert(profile.getName());
}
function logout()
{
alert('logging out');
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('User signed out.');
});
}
...
</head>
Code In Body (1st line from Google's tutorial listed above, 2nd line to trigger logout test)
<body>
...
<div class="g-signin2" data-onsuccess="onSignIn"></div>
<div onmousedown="logout()">Logout</div>
...
</body>
Is there some way I can include the google API on another page, and then call some check login status function? Or another way to concretely tell if the user is logged in or out?
You do not need to store anything on local storage. The library allows you to check if the user is logged in or not using the isSignedIn.get() on the auth2 of the gapi object.
Load the JavaScript library, make sure you are not deferring the load :
<script src="https://apis.google.com/js/platform.js"></script>
Then initialize the library and check if the user is logged in or not
var auth2;
var googleUser; // The current user
gapi.load('auth2', function(){
auth2 = gapi.auth2.init({
client_id: 'your-app-id.apps.googleusercontent.com'
});
auth2.attachClickHandler('signin-button', {}, onSuccess, onFailure);
auth2.isSignedIn.listen(signinChanged);
auth2.currentUser.listen(userChanged); // This is what you use to listen for user changes
});
var signinChanged = function (val) {
console.log('Signin state changed to ', val);
};
var onSuccess = function(user) {
console.log('Signed in as ' + user.getBasicProfile().getName());
// Redirect somewhere
};
var onFailure = function(error) {
console.log(error);
};
function signOut() {
auth2.signOut().then(function () {
console.log('User signed out.');
});
}
var userChanged = function (user) {
if(user.getId()){
// Do something here
}
};
Don't forget to change the app id
You can stringify a custom userEntity object and store it in sessionStorage where you can check it anytime you load a new page. I have not tested the following but it should work (doing something similar with WebAPI tokens in the same way)
function onSignIn(googleUser)
{
var profile = googleUser.getBasicProfile();
console.log('ID: ' + profile.getId());
console.log('Name: ' + profile.getName());
console.log('Image URL: ' + profile.getImageUrl());
console.log('Email: ' + profile.getEmail());
var myUserEntity = {};
myUserEntity.Id = profile.getId();
myUserEntity.Name = profile.getName();
//Store the entity object in sessionStorage where it will be accessible from all pages of your site.
sessionStorage.setItem('myUserEntity',JSON.stringify(myUserEntity));
alert(profile.getName());
}
function checkIfLoggedIn()
{
if(sessionStorage.getItem('myUserEntity') == null){
//Redirect to login page, no user entity available in sessionStorage
window.location.href='Login.html';
} else {
//User already logged in
var userEntity = {};
userEntity = JSON.parse(sessionStorage.getItem('myUserEntity'));
...
DoWhatever();
}
}
function logout()
{
//Don't forget to clear sessionStorage when user logs out
sessionStorage.clear();
}
Of course, you can have some internal checks if the sessionStorage object is tampered with. This approach should work with modern browsers like Chrome and Firefox.
To check is user Signed-in use:
gapi.auth2.getAuthInstance().isSignedIn.get()
Adding on to Joseph's answer above, you can then get information about the user by calling auth2.currentUser.get().getBasicProfile().
if (auth2.isSignedIn.get()) {
googleUserProfile = auth2.currentUser.get().getBasicProfile()
console.log('ID: ' + googleUserProfile.getId());
console.log('Full Name: ' + googleUserProfile.getName());
console.log('Given Name: ' + googleUserProfile.getGivenName());
console.log('Family Name: ' + googleUserProfile.getFamilyName());
console.log('Image URL: ' + googleUserProfile.getImageUrl());
console.log('Email: ' + googleUserProfile.getEmail());
}
From the docs: https://developers.google.com/identity/sign-in/web/people
according to the link from Phyrik post (https://developers.google.com/identity/sign-in/web/people) -
google stopped supporting the Sign-In Javascript web auth API:
We are discontinuing the Google Sign-In JavaScript Platform Library for web. The library will be unavailable for download after the March 31, 2023 deprecation date. Instead, use the new Google Identity Services for Web.
By default, newly created Client IDs are now blocked from using the older Platform Library, existing Client IDs are unaffected. New Client IDs created before July 29th, 2022 can set plugin_name to enable use of the Google Platform Library.
I know how to embed a feed which has a certain ID. I already did it. Now I'd like to implement the following functionality: If a user receives a private message, it will appear on an embedded feed. The best option in my opinion would be to embed the whole "chat window", but I didn't find a single code sample on the web. How can I do that?
You cannot really embed private messages like you can with feeds, because Yammer's REST APIs (incl. private messages) require authentication via OAuth 2.0. That means you have to create a Yammer API application which will ask your users to log in and allow you to access their messages. The overall concept of that described in their documentation here and here.
Yammer provides several SDKs you can use, one of them is the Javascript SDK. I pieced togehter a simple example of how you can ask users to log in and then it will display their private messages. Mind you, this is a very simple solution, I just tested it on a single one-to-one conversation.
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" data-app-id="YOUR-APP-CLIENT-ID" src="https://c64.assets-yammer.com/assets/platform_js_sdk.js"></script>
</head>
<body>
<span id="yammer-login"></span>
<div id="messages"></div>
<script>
yam.connect.loginButton('#yammer-login', function (resp) {
if (resp.authResponse) {
document.getElementById('yammer-login').innerHTML = 'Welcome to Yammer!';
}
});
var msgdiv = document.querySelector("#messages");
yam.getLoginStatus(
function(response) {
if (response.authResponse) {
console.log("logged in");
var myId = response.authResponse.user_id;
yam.platform.request({
url: "messages/private.json",
method: "GET",
success: function (response) {
console.log("The request was successful.");
var usernames = {};
response.references.forEach(function(ref){
if(ref.type === "user") {
usernames[ref.id] = ref.full_name;
}
});
response.messages.forEach(function(message){
var msg = document.createElement("span");
msg.innerHTML = usernames[message.sender_id] + ": " + message.body.parsed + "<br/>";
msgdiv.appendChild(msg);
})
},
error: function (response) {
console.log("There was an error with the request.");
console.dir(private);
}
});
}
else {
console.log("not logged in")
}
}
);
</script>
</body>
</html>
The response from the messages/private.json API endpoint is a JSON file that you can go through. It includes information about the message and the users involved in the conversation.
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.