简体   繁体   中英

Why gapi.auth.authorize with “immediate: false” doesn't close popup and fire callback?

I'm working on some chrome-extension. The Google Developer Console is configured and using gapi eventually works, but I've got problem with, let's say, UX.

So here is scenario I'm trying to acheive:

  1. Try to authenticate with Google Chrome logged in user.
  2. If fail, try to authenticate via gapi.auth.authorize with immediate: true.
  3. If fail, try to authenticate via gapi.auth.authorize with immediate: false.

And this kind of works. I get the popup which asks for permission, I click Accept, but then popus goes blank, title is set to "Connecting..." (doesn't close) and callback function is never fired.

I know access is granted because when I click accept and reload the page, it can authorize using immediate:true and my extension works perfectly.

I check few issues, topics and questions asking different queries in google searching for the answer and I found this sollutions:

  1. setTimeout(checkAuth, 1) - tried, no success.
  2. I deduced that immediate:false cannot be called right after immediate:true, so I give it a try and tried to authenticate with immediate:false as first. Same results.
  3. I tried adding gapi.auth.init, and checkingAuth in it's callback (also using setTimeout).

So here is bit of code (background.js). Sorry it looks like spaghetti, I'm beginner in JS.

function respond(interactive, sendResponse) {
    xhrWithAuth('GET',
        'https://www.googleapis.com/gmail/v1/users/me/profile',
        interactive, // false
        onUserMailFetched, sendResponse);

    function xhrWithAuth(method, url, interactive, callback, sendResponse) {
        var access_token;
        var retry = true;

        getToken();
        // 1. trying to use Chrome user
        function getToken() {
            chrome.identity.getAuthToken({
                interactive: interactive
            }, function (token) {
                if (chrome.runtime.lastError) {
                // 2. here lastError is User is not signed in. Calling onUserMailFetched
                    callback(chrome.runtime.lastError, null, null, sendResponse);
                }
                access_token = token;
                requestStart();
            });
        }
        // I guess not important in topic
        function requestStart() {
            var xhr = new XMLHttpRequest();
            xhr.open(method, url);
            xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
            xhr.onload = requestComplete;
            xhr.send();
        }
        // I guess not important in topic
        function requestComplete() {
            if (this.status == 401 && retry) {
                retry = false;
                chrome.identity.removeCachedAuthToken({
                        token: access_token
                    },
                    checkAuth_neverCalled);
            } else {
                callback(null, this.status, this.response, sendResponse);
            }
        }
        // I guess not important in topic
        function checkAuth_neverCalled() {
            console.log("checking auth when getAuthToken fails");
            gapi.auth.authorize({
                client_id: OAUTH2_CLIENT_ID,
                scope: OAUTH2_SCOPES,
                immediate: false
            }, handleAuthResult);

            // Handle the result of a gapi.auth.authorize() call.
            function handleAuthResult(authResult) {
                console.log("authenticated: ", authResult);
                if (authResult) {
                    // do something with data
                } else {
                    consoel.log("failed");
                }
            }
        }
    }

    // This is important part.
    function onUserMailFetched(error, status, response, sendResponse) {
        if (!error && status == 200) {
            // do something with data
        } else {
         // 3. as we have error at first call, we checkAuth with immediate = true
            setTimeout(function () {
                checkAuthWhenNotLogged(sendResponse, true);
            }, 10);
        }
    }
    // This is important part.
    function checkAuthWhenNotLogged(sendResponse, immediateVal) {
        gapi.auth.authorize({
            client_id: OAUTH2_CLIENT_ID,
            scope: OAUTH2_SCOPES,
            immediate: immediateVal
        }, handleAuthResult);

        // Handle the result of a gapi.auth.authorize() call.
        // 5. But this function is never called again (when called with false).
        function handleAuthResult(authResult) {
            if (authResult) {
            // 4. and this is called when checkAuth with true fail. We call checkAuth (itself) with false.
                if (authResult.error == "immediate_failed") {
                    gapi.auth.init(function () {
                        setTimeout(function () {
                            checkAuthWhenNotLogged(sendResponse, false);
                        }, 10);
                    });
                } else {
                // yay, we are authneticated and can call gmail service
                    gapi.client.load('gmail', 'v1', function () {
                        var request = gapi.client.gmail.users.getProfile({
                            'userId': 'me'
                        });
                        request.execute(function (profile) {
                            // do something with data
                        });
                    });
                }
            } else {
                console.log("failed");
            }
        }
    }
}

Any hint, link or solution will be apreciated.

Ok, here is what I did to make OAuth2 work.

Scenario looks like:

  1. Try to authenticate with Google Chrome logged in user.
  2. If fail, try to authenticate via gapi.auth.authorize with immediate: true.
  3. If fail, try to use chrome.identity.launchWebAuthFlow

First of all I need to explain why launchWebAuthFlow wasn't working earlier. As I mention, I configured the Google Developers Console and created key and client id as for Chrome Application. This was wrong for launchWebAuthFlow. It should be Web Application with configured redirect URL.

In chrome extension here is how you can get redirect url: var redirectURL = chrome.identity.getRedirectURL("suffix"); It will create something like this: https://{appId}.chromiumapp.org/

You need to set this as redirect link in your Client ID configuration. In my case I had to change used Client ID in js code.

Here is code that works for me:

(...)
var redirectURL = chrome.identity.getRedirectURL();
var options = {
    'interactive': true,
    url: 'https://accounts.google.com/o/oauth2/auth?' +
        'scope=profile email' +
        '&response_type=token' +
        '&client_id=' + OAUTH2_CLIENT_ID_WEB +
        '&redirect_uri=' + redirectURL
}
chrome.identity.launchWebAuthFlow(options, function (redirectUri1) {
    if (chrome.runtime.lastError) {
        console.log(chrome.runtime.lastError);
    } else {
        // redirectUri is a link with access_token param inside, we need to extract it
        var paramName = "access_token"
        var results = new RegExp(paramName + '=([^&#]*)').exec(redirectUri1);
        if (results == null) {
            console.log("not found");
        } else {
            console.log(results[1] || 0);
            access_token = results[1]; // here we set the token
            requestStart(); // here I launch google api request
        }
    };
});
(...)
function requestStart() {
        // url = 'https://www.googleapis.com/plus/v1/people/me'
        // method = 'GET'
        var xhr = new XMLHttpRequest();
        xhr.open(method, url);
        xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
        xhr.onload = requestComplete;
        xhr.send();
    }

function requestComplete() {
    if (this.status == 401 && retry) {
        retry = false;
        chrome.identity.removeCachedAuthToken({
                token: access_token
            },
            checkAuth);
    } else {
        callback(this.status, this.response);
    }
}

Hope someone will take advantage of this. I know I spent way too much time on this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM