Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google picker auth popup is being blocked

I have a mobile site which lists jobs, the user applies and uploads their CV (resume) - I want them to be able to choose a file from their Google Drive.

I've created the Hello world example here - https://developers.google.com/picker/docs/ (code reproduced here for convenience)

Problem is that if not already logged into Drive, a popup to login is launched. This is bad enough on a desktop but really bad on a phone.

I have tried this solution, but get 'TypeError: gapi.auth is undefined'

I also tried launching the picker from an onclick event rather than the onload as described by the docs.

function launchDrive()
    {
        gapi.load('auth', {'callback': onAuthApiLoad});
        gapi.load('picker', {'callback': onPickerApiLoad});
    }
<input type='button' value='Launch Drive' onclick='launchDrive();'>

Sample Google code:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>Google Picker Example</title>

    <script type="text/javascript">

      var developerKey = 'xxxxxxxYYYYYYYY-12345678';
      var clientId = "1234567890-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com"
      var scope = ['https://www.googleapis.com/auth/photos'];

      var pickerApiLoaded = false;
      var oauthToken;

      function onApiLoad() {
        gapi.load('auth', {'callback': onAuthApiLoad});
        gapi.load('picker', {'callback': onPickerApiLoad});
      }

      function onAuthApiLoad() {
        window.gapi.auth.authorize(
            {
              'client_id': clientId,
              'scope': scope,
              'immediate': false
            },
            handleAuthResult);
      }

      function onPickerApiLoad() {
        pickerApiLoaded = true;
        createPicker();
      }

      function handleAuthResult(authResult) {
        if (authResult && !authResult.error) {
          oauthToken = authResult.access_token;
          createPicker();
        }
      }

      // Create and render a Picker object for picking user Photos.
      function createPicker() {
        if (pickerApiLoaded && oauthToken) {
          var picker = new google.picker.PickerBuilder().
              addView(google.picker.ViewId.PHOTOS).
              setOAuthToken(oauthToken).
              setDeveloperKey(developerKey).
              setCallback(pickerCallback).
              build();
          picker.setVisible(true);
        }
      }

      // A simple callback implementation.
      function pickerCallback(data) {
        var url = 'nothing';
        if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
          var doc = data[google.picker.Response.DOCUMENTS][0];
          url = doc[google.picker.Document.URL];
        }
        var message = 'You picked: ' + url;
        document.getElementById('result').innerHTML = message;
      }
    </script>
  </head>
  <body>
    <div id="result"></div>

    <!-- The Google API Loader script. -->
    <script type="text/javascript" src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
  </body>
</html>

13 May 2015 edit

Further to Jason's answer, here is what I also tried (called by a button oncllick):

function launchDrive()
    {
        //gapi.load('auth', {'callback': onAuthApiLoad});
        gapi.auth.init(onAuthApiLoad);
        gapi.load('picker', {'callback': onPickerApiLoad});
    }
like image 884
KevInSol Avatar asked May 08 '15 14:05

KevInSol


3 Answers

I have it working now.

In the example for the picker, https://developers.google.com/picker/docs/, it calls:

 <script type="text/javascript" src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>

In this example, https://developers.google.com/api-client-library/javascript/start/start-js, it calls:

<script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script>

Using client.js fixes the 'TypeError: gapi.auth is undefined' issue, and thus the login popup works.

Maybe api.js is an older version of the API?

like image 73
KevInSol Avatar answered Nov 13 '22 08:11

KevInSol


You will want to call gapi.auth.init. See the docs here: https://developers.google.com/api-client-library/javascript/reference/referencedocs#gapiauthinit

Initializes the authorization feature. Call this when the client loads to prevent popup blockers from blocking the auth window on gapi.auth.authorize calls.

like image 20
Jason Clawson Avatar answered Nov 13 '22 08:11

Jason Clawson


To solve your issue you need to understand how google performs oauth in your case:

  • gapi performs init actions
  • gapi opens a google auth page in new popup window and you perform login in it
  • after login succeeded gapi gets notified and you receive your token

Why browser blocks the popup in 2nd step:

  • original event is not present anymore in window (window.event is destroyed).
  • User manually blocked the popup from current site

So if user didn't block a popup and you popup is still blocked, gapi actions look something like:

<input type="button" onclick="auth" value="click"/>

function auth() {
  setTimeout(function() {
     // by this time window.event is destroyed, that's why browser blocks the popup
     window.open(document.URL, '_blank', 'location=yes,height=570,width=520,scrollbars=yes,status=yes');
  }, 100)
}

So what you should do:

  • Make sure that after button click you don't perform any asynchronous actions like XHRequests or other
  • Make sure gapi is inited and ready by the time users click on the button, so by the time gapi needs to create a popup, window.event won't be null. So move all gapi init methods to DOMContentLoaded.
  • As another option you could use server-side oauth flow, which means instead of popup user will be redirected in current tab to gauth page.
like image 27
deathangel908 Avatar answered Nov 13 '22 08:11

deathangel908