Google+ Login via JavaScript and PHP

Google+ login via Javascript and PHP

Once you have an authenticated client for the user you will want to make a request to the Google+ people.get API method.

$client = new Google_Client();
$client->setClientId(CLIENT_ID);
$client->setClientSecret(CLIENT_SECRET);
$client->setRedirectUri(REDIRECT_URI);
$client->setAccessToken($access_token);
$plus = new Google_Service_Plus($client);
$me = $plus->people->get('me');
print "ID: {$me['id']}\n";
print "Display Name: {$me['displayName']}\n";
print "Image Url: {$me['image']['url']}\n";
print "Url: {$me['url']}\n";

For their email you will have to iterate over the $me['emails'] array and find the value with type=account.

Google login in PHP backend and JS frontend

  1. You don't need to set scopes in this case.
  2. (see answer 3, but also): Check your client ID matches the one used in the Javascript, and that the client secret is exactly as in the console (no trailing/leading spaces).
  3. Changing your redirecturi to 'postmessage' - this is the string used when the code was generated via the Javascript process.

You can also try manually constructing the URL and calling it with curl to make sure everything is as you expect: https://developers.google.com/accounts/docs/OAuth2WebServer#handlingtheresponse

How to integrate Google one tap sign in?

Looks good so far, though I would recommend against sharing your clientID and ClientSecret on a public forum.

Google provide instructions for integrating their sign-in service.
I recommend following those instructions. This will require the following files:

  1. A login page which contains the google sign-in button. You could conceivably add this to any of your existing pages. The relevant code is:
<div class="g-signin2" data-longtitle="true" data-onsuccess="onSignIn"></div>

  1. A javascript file which contains the onSignIn function and a signOut function if you want one. This file handles the redirect to a successful logged in page and also passes the attributes you want to collect from the user's Google account. I'm using XMLHttpRequest, but you could use POST if you wish. This page contains the page that the user will be directed to upon successful login, set in xhr.onreadystatechange = function() {}:
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
var id_token = googleUser.getAuthResponse().id_token;
// console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.
var xhr = new XMLHttpRequest();
xhr.open('POST', 'includes/oauth.php');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
window.location = "../loggedin.php"; //Redirect to loggedin page on completion of oauth.php. Determine if new user or existing user and process accordingly
}
xhr.send('idtoken=' + id_token + '&googleId=' + profile.getId() + '&name=' + profile.getName() + '&imageURL=' + profile.getImageUrl() + '&email=' + profile.getEmail());
}

function signOut() {
gapi.load('auth2', function() {
gapi.auth2.init().then(function(){
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
document.location.href = 'includes/logout.php';
});
});
});
}

  1. A file to handle the authentication (referred to as includes/oauth.php in my javascript file above). Note the settings for $leeway - this caused me a lot of grief figuring out that the clock on my server was slower than the Google auth server's clock!):
require_once '../vendor/autoload.php';
$jwt = new \Firebase\JWT\JWT; //Allow for discrepancies between server and auth times
$jwt::$leeway = 60;

$CLIENT_ID = "ENTER_YOUR_CLIENT_ID_HERE";
$client = new Google_Client(['client_id' => $CLIENT_ID]); // Specify the CLIENT_ID of the app that accesses the backend
$client->setRedirectUri("http://localhost/includes/oauth.php");
$client->addScope("email");
$client->addScope("profile");

if (isset($_POST['idtoken'])){
$id_token = $_POST['idtoken'];
$attempt = 0;
do {
try {
$payload = $client->verifyIdToken($id_token);
$retry = false;
} catch (Firebase\JWT\BeforeValidException $e) {
error_log("JWT server time mismatch. Retry attempt: " . strval($attempt) . "Error: " . $e, 0);
$attempt++;
$retry = $attempt < 3;
}
} while ($retry);

if ($payload) {
$userid = $payload['sub'];
...
YOUR VALIDATION, SESSION SETTING, ETC. CODE HERE
...
} else {
// Invalid ID token
print("Invalid ID token");
}
} else { //Attempt to access this page directly, redirect to Google login page
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
}


  1. The page that will displayed upon successful login. I used an interstitial page here because the authenticated user could be new to my site and need to create a profile, or could be an existing user and want to go about their activities. I look to verify whether a SESSION has been started and whether this includes a successful authentication.

Creating a Google login page to access services

The reason you are being redirected is because you are essentially loading google's page into an iframe. Everything that is normally called on their login page on submit is called when you input. So basically you're just opening a window to google within your website.

The correct way to implement this would be to use google's oauth API. (https://developers.google.com/accounts/docs/OAuth2 or more specifically, https://developers.google.com/accounts/docs/OAuth2Login)

You'll need to register a project and get a developer/API key.

The google docs are very comprehensive, so you should be able to follow them pretty easily!

Authenticate using Google javascript API

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 Topics



Leave a reply



Submit