4

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']);

1
  • invalid_grant normally means that either something is wrong with the clock on the server, or the refresh token limit has been exceeded. Commented Oct 1, 2014 at 11:51

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.