1

I'm using the Google Identity Platform's OAuth 2.0 flow to authorize a javascript/HTML teacher observation form to write to a Google Sheets document. Everything is working well most of the time; however, last night one of our principals hit the following error:

"Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project."

I determined that he had launched the observation tool in the afternoon, and now maybe five hours later was trying to click the submit button. My hunch was that the token had expired, but from Google's documentation it seems like the JS auth library is meant to handle refreshing the access token as necessary - I believe it's not actually possible to get a refresh token to do anything manually.

I'm using what is essentially the sample auth code, and the app responds to being signed out appropriately. That is, if I sign out in another tab, the submit button is disabled and the sign-in button appears again. Assuming token expiration is the issue here, any ideas on the correct way to identify if the token has expired and how to request a new one, ideally without user interaction? Or if it's not an expiration issue, what else could it be? This user has successfully submitted data in earlier observations; it was just this one time when he waited ~5 hours (potentially losing internet connectivity / sleeping his laptop) during that time.

Here's the auth code:

var clientId = ""; //id removed
var discoveryDocs = ["https://sheets.googleapis.com/$discovery/rest?version=v4"];
var scopes = "https://www.googleapis.com/auth/spreadsheets";
var authorizeButton = document.getElementById('authorize-button');
function handleClientLoad() {
    gapi.load('client:auth2', initClient);
}
function initClient() {
    gapi.client.init({
        discoveryDocs: discoveryDocs,
        clientId: clientId,
        scope: scopes
    }).then(function () {
        gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
        updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
        authorizeButton.onclick = handleAuthClick;
    });
}
function updateSigninStatus(isSignedIn) {
    if (isSignedIn) {
        authorizeButton.style.display = 'none';
        document.getElementById('submit').disabled = false;
        findRow(); //find the empty row once we're logged in
    } else {
        authorizeButton.style.display = 'block';
        document.getElementById('submit').disabled = true;
    }
}
function handleAuthClick(event) {
    gapi.auth2.getAuthInstance().signIn();
}

Thank you!

Eli Sheldon
  • 11
  • 1
  • 3

4 Answers4

0

Similar issues that i had resulted in issues from that Authorized Javascript origins. "In the Authorized JavaScript origins field, enter the origin for your app. You can enter multiple origins to allow for your app to run on different protocols, domains, or subdomains. You cannot use wildcards. In the example below, the second URL could be a production URL." taken from https://developers.google.com/identity/sign-in/web/devconsole-project.If prompt to view task came from an email, the email origin must be verified -or- the device is used for multiple accounts, the token will not stay. If the api is being improperly used, it will allow functionality for a short period of time , then fail.

  • I don't think this is the problem I'm facing - the tool is at a static production URL and it works from the same URL consistently. I've found now that access tokens expire after an hour, and while the JS library is meant to refresh it automatically, that may not happen correctly if the computer is asleep or offline. I'm looking at https://stackoverflow.com/questions/32150845/how-to-refresh-expired-google-sign-in-logins now as a way to force a refresh if the token has expired! – Eli Sheldon Oct 03 '17 at 17:56
0

This may be useful, in the authflow, you do not have scope or id in options

   /** * Initiate auth flow in response to user clicking authorize button. * * 
   @param {Event} event Button click event. */ function 
   handleAuthClick(event) { 
   gapi.auth.authorize( {client_id: '[@app:client_id]', scope: 
   ["googleapis.com/auth/calendar"], immediate: false}, handleAuthResult); 
   return false; }
0

I believe How to refresh expired google sign-in logins? had the answer I needed. Since all of my API calls happen at once, I added a new Date() when the page loads, a second new Date() when the submission flow begins, and if they are more than 45min (2,700,700ms) apart, I use gapi.auth2.getAuthInstance().currentUser.get().reloadAuthResponse() to force an access token refresh, as documented at https://developers.google.com/identity/sign-in/web/reference#googleuserreloadauthresponse.

Hopefully Google will eventually update their documentation to reflect this now-necessary step when using the auth2 flow vs the older auth flow.

Time will tell if this actually solved the issue, but I'm hopeful!

Eli Sheldon
  • 11
  • 1
  • 3
0

I hope it helps you friend that error is because you have the wrong time, you go to date and time settings then press synchronize now.