1

401 Invalid response: This seems to be a reasonably common issue judging by the Google search responses, but unfortunately none of those have helped me to resolve my issue fully.

I have a web page which requires a Google based login, and then selects data from a fusion table. During the login process I check that the user has the appropriate access to the fusion table, so I know that the login has worked and that they are a valid user allowed to edit that table (shared to specific people). I also check that the token received is valid. So for each successful login I know that they have permission to access the table (it has been shared with them), and a valid token (they have signed in).

As this is a JavaScript app there is no Refresh Token, just the access token (online access). The prompt does say offline access but I think this is just a Google default thing. I do not set the APIKey, which resolved another 401 error that some users (not the same ones unfortunately) had (from this stack overflow question). The request works fine in the Google Oauth developers console.

Most users can access it fine, but for some users when they run the query to select data from the fusion table they always get a 401 error when the GET request is sent (shows up as 401 (Unathorized) in the console and 401: Invalid Credentials in the error returned). I cannot reproduce the problem on my machine however and am stuck as to why it is happening and what I can do next.

It is very difficult to remote to the users machine, and I cannot install software on it such as Fiddler to view the headers, hence the console logging.

I have attached an image of the users console with a failed request. The GET request looks exactly the same on my machine (where it works fine). You can see that the Token is valid, they are logged in etc.

enter image description here

Here is a cut down version of the relevant code (sensitive information removed) and with extra debugging code in there like checkAuth, validateToken and handleAuthResult2:

var clientId = 'my_client_id.apps.googleusercontent.com';
var ftTableID = 'fusiontableidstring';
var scopes = 'https://www.googleapis.com/auth/fusiontables https://www.googleapis.com/auth/drive.metadata.readonly  https://www.googleapis.com/auth/userinfo.email';
var accessToken;  
var email;
var name;  
var params = {
    'clientid': clientId,
    'cookiepolicy': 'single_host_origin',
    'scope': scopes,
    'approvalprompt': 'force',
    'include_granted_scopes': true,
    'callback': handleAuthResult
    };
gapi.auth.signIn(params);

//debug code
function validateToken() {
    $.ajax({
        url: 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=' + accessToken,
        data: null,
        success: function(response){
            console.log(response)
            if (response.audience == clientId) {
                console.log('Our token is valid....');
            } else {
                console.log(response.audience + ' not equal to ' + clientId);
            }
        },  
        error: function(error) {
            console.log(error)
            console.log('Our token is not valid....');
        },
        dataType: "jsonp" 
    });
}

function checkAuth() {
    console.log("checking authorization");
    gapi.auth.authorize(
        {'client_id': clientId, 'scope': scopes, 'immediate': true},
        handleAuthResult2);
}

function handleAuthResult2(authResult) {
    if (authResult) { //result
        console.log(authResult.status);
        console.log(authResult);

        if (authResult.error) {
            reset();
            gapi.auth.authorize(
                {'client_id': clientId, 'scope': scopes, 'immediate': false},
                handleAuthResult);
        } else {
        }
    }
}
//end debug code

// Handle the results of the OAuth 2.0 flow.
function handleAuthResult(authResult) {
    if (authResult) { //result
        if (authResult.error) {
            console.log(authResult.error);
            reset();
        } else {
            //successfully signed in, requests can be sent (will automatically contain access token)
            accessToken = authResult.access_token;
            validateToken(); //debug only
            //check who the user is
            gapi.client.load('plus','v1').then(function() {
                gapi.client.plus.people.get({
                    'userId': 'me'
                }).then(function(res) {
                    var profile = res.result;
                    email = profile.emails[0].value;
                    name = profile.displayName;
                    document.getElementById("signinconfirmation").innerHTML = "You are signed in as " + email + "(" + name + ")" ;
                 //check permissions
                    }).then(function() {
                        gapi.client.load('drive', 'v2').then(function() {
                            gapi.client.drive.permissions.getIdForEmail({
                                'email': email,
                            }).then(function(resp) {
                                var userid = resp.result.id;
                                gapi.client.drive.permissions.get({
                                    'fileId': ftTableID,
                                    'permissionId': userid
                                }).then(function(resp2) {
                                    if(resp2.status == 200) {
                                    document.getElementById("signinconfirmation").innerHTML = "You are signed in as " + email + "(" + name + ") with correct permissions." ;
                                    } else {
                                    document.getElementById("signinconfirmation").innerHTML = name + " at " + email + " does not have the appropriate permissions to edit this table.";
                                    } 
                                },
                                 function(reason) { //drive permissions failed
                                document.getElementById("signinconfirmation").innerHTML = name + " at " + email + " does not have the appropriate permissions to edit this table.";
                                });
                        });
                    });
                });
            });
        }
    }
}

// build the request to SELECT data from Fusion Table.
function selectData() {
        var sqlValue = document.getElementById("searchvalue").value;
        var matchCondition = "CONTAINS";
        if (sqlValue.length == 0) {
            alert('No value entered!')
        } else {
            //escape single quotes in sqlValue
            sqlValue = sqlValue.replace(/'/g, "\\'");
            var sql = "SELECT ROWID, COUNTRY FROM " + ftTableID + " WHERE COUNTRY " + matchCondition + " '" + sqlValue + "' ORDER BY ADMINNAME";  
            query(sql, 'select-data-output');
        };
}
// Send SQL query to Fusion Tables.
function query(query, element) {
    var lowerCaseQuery = query.toLowerCase();
    var path = '/fusiontables/v2/query';
    var callback = function(element) {
        //handle response depending on output element
        return function(resp) {
            if (resp.rows) {
                createTable(resp, element);
            } else {
                //no rows returned
                if (resp.error) {
                    var tableID = element;
                    clearTable(tableID);
                    var tableDiv = document.getElementById(tableParentDiv);
                    var tableElement = document.createElement('table');
                    tableElement.id = tableID;

                    if (resp.error.code) {
                        var errorText = "Error code: " + resp.error.code;
                        errorText += '\n' + resp.error.message;
                        tableElement.innerHTML = errorText;
                    } else {
                        tableElement.innerHTML = "An unknown error occurred " + resp.error;
                    }
                    tableDiv.appendChild(tableElement);
                    if (resp.error.code == 401) {
                        checkAuth(); //debug only
                    }

                } else {
                    alert("No results for that search. Check spelling and select from options.");
                     clearTable(element);
                } 
            }
        };
    }
    if (lowerCaseQuery.indexOf('select') != 0 &&
        lowerCaseQuery.indexOf('show') != 0 &&
        lowerCaseQuery.indexOf('describe') != 0) {
        // is update query
        var body = 'sql=' + encodeURIComponent(query);
        runClientRequest({
            path: path,
            body: body,
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded',
              'Content-Length': body.length
            },
            method: 'POST'
        }, callback(element));
    } else {
        runClientRequest({
            path: path,
            params: { 'sql': query }
        }, callback(element));
    }
}

// Execute the client request.
function runClientRequest(request, callback) {
    var restRequest = gapi.client.request(request);
    console.log("the restRequest is: " + JSON.stringify(restRequest));
    restRequest.execute(callback);
}
Community
  • 1
  • 1
spg
  • 151
  • 8
  • Found the issue, it was unrelated to the OAuth process and a problem with the google apps domain permissions for the organization blocking access. – spg Apr 17 '15 at 18:39

1 Answers1

0

For posterity: The issue was not the OAuth process, it was a problem with the user not having the correct google apps domain permissions set. When the organization opened the fusion table service for the users subdomain, the error went away.

spg
  • 151
  • 8