Google picker auth popup is being blocked

后端 未结 4 2059

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 create

相关标签:
4条回答
  • 2021-01-02 07:09

    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.

    0 讨论(0)
  • 2021-01-02 07:13

    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.
    0 讨论(0)
  • 2021-01-02 07:14

    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?

    0 讨论(0)
  • 2021-01-02 07:15

    Just Skip to the bottom

    Here's the code that works for me currently. This is my first hour using this API though, so I really don't know what any of these functions do yet, nor do I know what the proper order and error handling is just yet, but at least this is functional now. Maybe it'll help someone else in the future.

    <!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>
    </head>
    <body style="width: 70%; margin: 100px auto;">
    
        <!-- Added a button to open picker -->
        <button onclick="loadPicker();" >Open from GoogleDrive</button>
        <div id="result"></div>
    
        <!-- Moved to end of body tag instead of head -->
        <script type="text/javascript">
    
        // The Browser API key obtained from the Google API Console.
        // Replace with your own Browser API key, or your own key.
        var developerKey = '<IDK WHAT'S SUPPOSED TO GO HERE, BUT ITS OK>';
    
        // The Client ID obtained from the Google API Console. Replace with your own Client ID.
        var clientId = "<YOUR CLIENT ID GOES HERE>.apps.googleusercontent.com"
    
        // Replace with your own project number from console.developers.google.com.
        // See "Project number" under "IAM & Admin" > "Settings"
        var appId = "<YOUR APP ID GOES HERE>";
    
        // Scope to use to access user's Drive items.
        var scope = ['https://www.googleapis.com/auth/drive'];
    
        var pickerApiLoaded = false;
        var oauthToken;
    
        // Use the Google API Loader script to load the google.picker script.
        function loadPicker() {
            // This needs to be client:auth2 no client
            gapi.load('client:auth2', {'callback': onAuthApiLoad});
            gapi.load('picker', {'callback': onPickerApiLoad});
        }
    
        function onAuthApiLoad() {
            // we need to init gapi.client with the clientId and scope first
            gapi.client.init({
                clientId: clientId,
                scope: scope
            });
    
            // Now we can authorize? seems like the same thing here
            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 searching images.
        function createPicker() {
            // Wow this is a mess
            if (pickerApiLoaded && oauthToken) {
                var view = new google.picker.View(google.picker.ViewId.DOCS);
                view.setMimeTypes("image/png,image/jpeg,image/jpg");
                var picker = new google.picker.PickerBuilder()
                .enableFeature(google.picker.Feature.NAV_HIDDEN)
                .enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
                .setAppId(appId)
                .setOAuthToken(oauthToken)
                .addView(view)
                .addView(new google.picker.DocsUploadView())
                // Guess this is... optional?
                //.setDeveloperKey(developerKey)
                .setCallback(pickerCallback)
                .build();
                picker.setVisible(true);
            }
        }
    
        // A simple callback implementation.
        function pickerCallback(data) {
            if (data.action == google.picker.Action.PICKED) {
                var fileId = data.docs[0].id;
                alert('Selected fileId: ' + fileId);
            }
        }
        </script>
    
        <!-- The Google API Loader script. Removed the autorun -->
        <script type="text/javascript" src="https://apis.google.com/js/api.js"></script>
    </body>
    </html>
    

    Edit: If you get a pop-up window that doesn't load, just close it and click the button again. That fixed another issue I just had.

    Again, I don't know what I'm doing yet, so hopefully I can get a better understanding of this and clarify things later.

    E2: Ah, there's more information about OAuth2 over on the Javascript GAPI documentation page which can be found here: https://developers.google.com/api-client-library/javascript/features/authentication

    From another document, it appear that gapi.load('client', callback) will load auth2 if not already loaded. Calling gapi.load('client:auth2', callback) will just save 1 network request.

    Note: when you authorize your application using Oauth 2.0, you do not also need to set the API key as in the first example. However, it is a good practice to do so, in case your code ever expands to handle unauthorized requests.

    That explains why I could remove the API/developer key.

    Edit 3: Ok the above code is technically wrong.

    Warning: do not use this method alongside the recommended gapi.auth2.init and signIn flow. These are two distinct behaviors (Authorization for gapi.auth2.authorize vs Authentication for gapi.auth2.init/signIn) and will have unexpected issues if used within the same application.

    autorize is for single use authentications (if you were logged into 2 google accounts for instance). While using gapi.init() is meant to be for a more long term session (like for logging in and out of a website).

    How this is working currently, I do not know.


    Don't use the above code, just wanted to document the progress. Here's a better demo working with getAuthResponse()

    <html>
      <head></head>
      <body>
        <div style="padding: 50px">
            <h2 style="color: #2196f3;">Status: <span id='status'></span></h2>
            <button id="signin-button" onclick="handleSignInClick()">Sign In</button>
            <button id="signout-button" onclick="handleSignOutClick()">Sign Out</button>
            <button id="signout-button" onclick="openFile()">Open File</button>
        </div>
    
        <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
        <script type="text/javascript">
        var cid = '<CLIENTID_HERE>';
        var scope = 'https://www.googleapis.com/auth/drive';
        var authenticated = false;
        var pickerLoaded = false;
        var auth = null;
        var user = null;
        var response = null;
        var token = null;
        var stat = $('#status');
    
          function openFile() {
            gapi.load('client:auth2', initClient);
            gapi.load('picker', onPickerLoad);
          }
    
            function initClient() {
                stat.html("starting");
    
                gapi.client.init({
                    clientId: cid,
                    scope: scope
                }).then(
                function () {
                    console.log("init");
    
                    // Check if we are logged in.
                    auth = gapi.auth2.getAuthInstance();
                    auth.isSignedIn.listen(onStatusChange);
                    authenticated = auth.isSignedIn.get();
                    stat.html(authenticated);
    
                    if (authenticated) {
                        stat.html("Logged In!");
                        user = auth.currentUser.get();
                        response = user.getAuthResponse(true);
                        token = response.access_token;
                        showPicker();
                    } else {
                        stat.html("Logged Out!");
                    }
                }, function(){stat.html("error")});
            }
    
            function onStatusChange(isSignedIn) {
                if (isSignedIn) {
                    stat.html("Logged In!");
                    authenticated = true;
                    user = auth.currentUser.get();
                    response = user.getAuthResponse(true);
                    token = response.access_token;
                    showPicker();
                    showPicker();
                } else {
                    authenticated = false;
                    stat.html("Logged Out!");
    
                }
            }
    
            function handleSignInClick(event) {
                gapi.auth2.getAuthInstance().signIn();
            }
    
            function handleSignOutClick(event) {
                gapi.auth2.getAuthInstance().signOut();
                alert("signed out");
            }
    
    
            function onPickerLoad() {
                pickerLoaded = true;
                showPicker();
            }
    
            function showPicker() {
                if (pickerLoaded && authenticated) {
                    var view = new google.picker.View(google.picker.ViewId.DOCS);
                    var picker = new google.picker.PickerBuilder();
                    picker.addView(view);
                    picker.enableFeature(google.picker.Feature.MULTISELECT_ENABLED);
                    picker.setOAuthToken(token);
                    picker.setAppId()
                    picker.setCallback(onDriveFileOpen);
                    picker = picker.build();
                    picker.setVisible(true);
                }
            }
    
            function onDriveFileOpen(data) {
                console.log(data);
                if (data.action == google.picker.Action.PICKED) {
                    var fileId = data.docs[0].id;
                    console.log(fileId);
                    alert(data.docs[0].name);
                }
            }
    
        </script>
        <script async defer src="https://apis.google.com/js/api.js">
        </script>
      </body>
    </html>
    
    0 讨论(0)
提交回复
热议问题