Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Drive Realtime API OAuth2 Refresh Errors

Over the last couple days I've been seeing some increasingly problematic issues with a long running realtime app. I've walked through the steps below and the code snippet at the bottom contains additional debug info from the app.

When the page first opens it successfully requests an OAuth token and loads the realtime doc [A]. After 50 minutes (10 minutes before the token expires) it re-requests a new OAuth token successfully [B]. After the first token has expired the currently open connection gets a 401 unauthorized error and requires a new oauth token [C]. This in itself seems like a problem as it should have updated itself to use the new valid token from [B].

However the app should still be resilient to an error like this occurring - so it gets handled by closing and reopening the doc [D] and getting another new OAuth token. Unfortunately at this point, the realtime API sits in an infinite loop getting errors for the access_token [E].

All of the OAuth tokens are requested using gapi.auth.authorize with the same scopes and no call to setToken. I previously tried using setToken, but that had exactly the same problems.

Actual Questions What is the correct way to deal with refreshing the OAuth token for the realtime API? How can I prevent repeated failures internal to the drive APIs when closing and reopening a doc?

[A]
_aa: "1"
access_token: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXz9AYBkyympssqI"
client_id: "XXXXXXXXXXXXXXXX.apps.googleusercontent.com"
cookie_policy: undefined
expires_at: "1373610287"
expires_in: "3600"
g_user_cookie_policy: undefined
issued_at: "1373606687"
response_type: "token"
scope: Array[2]
state: ""
token_type: "Bearer"

[B]
_aa: "1"
access_token: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXV2kzG4EMUppi"
client_id: "XXXXXXXXXXXXXX.apps.googleusercontent.com"
cookie_policy: undefined
expires_at: "1373613288"
expires_in: "3600"
g_user_cookie_policy: undefined
issued_at: "1373609688"
response_type: "token"
scope: Array[2]
state: ""
token_type: "Bearer"

[C]
GET https://drive.google.com/otservice/bind?id=1B-XXXXXXXXXXXXXXXXXXXXX_nRizfqmT…&RID=rpc&SID=XXXXXXXXXXXXXXXXX&CI=0&AID=221&TYPE=xmlhttp&zx=ns6e5dr7rf4&t=1 401 (Unauthorized)

Drive Realtime API Error: token_refresh_required: The OAuth token must be refreshed. 

[D]
[Close Realtime Document]
[Open Realtime Document]

_aa: "1"
access_token: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXMHzJXm2dF-"
client_id: "XXXXXXXXXXXXXX.apps.googleusercontent.com"
cookie_policy: undefined
expires_at: "1373613918"
expires_in: "3600"
g_user_cookie_policy: undefined
issued_at: "1373610318"
response_type: "token"
scope: Array[2]
state: ""
token_type: "Bearer"

[E]
[x100] Uncaught TypeError: Cannot read property 'o' of null 

Thanks!

like image 649
Grant Watters Avatar asked Jul 12 '13 20:07

Grant Watters


People also ask

How do I get a new refresh token OAuth2?

Use the code you get after a user authorizes your app to get an access token and refresh token. The access token will be used to authenticate requests that your app makes. Access tokens are short lived, so you can use the refresh token to get a new access token when the current access token expires.

How does OAuth2 refresh token work?

The Refresh Token grant type is used by clients to exchange a refresh token for an access token when the access token has expired. This allows clients to continue to have a valid access token without further interaction with the user.


1 Answers

I took a look at your issue and I think you're correct about the two main issues.

  1. Refreshed tokens aren't automatically picked up by the API.
  2. Closing and reopening a document leads to periodic "Cannot read property 'o' of null" errors.

Regarding issue 1, we pick up a refreshed token any time a change is saved and also every second while the server to client connection is unhealthy (e.g. after a 401 error). What this obviously doesn't cover is the case where you refresh the token early and there isn't any change to the document. In this case, you'll see a 401 even though you already updated the token. I'm working on a fix for this issue that will pick up the refreshed token every 30 seconds while the connection is healthy. Ultimately we would like to have this be event driven so that the token is picked up immediately, but that is somewhat more involved because it will require changes to gapi.auth.

Regarding issue 2, based on my testing it appears that the periodic error is spurious (internally we're still trying to refresh the token for the old document even though the document has been closed, which is why you get the 'Cannot read property' error). I'm working on a fix for this as well, but you should still be able to reload the document (although you will see lots of garbage errors for the old document). Please let me know if this isn't the case and you really can't reload the document.

I should note that you should not need to reload the document when you get a token refresh error. This is indicated by the fact that the isFatal property of the gapi.drive.realtime.Error object passed to your error handler is false, which indicates that the error is recoverable. The suggested response for a token_refresh_required error is to refresh the token - the network service should catch up automatically. If that doesn't work, please feel free to let me know as it is a bug.

-- Brian (Realtime API Developer)

like image 169
Brian Cairns Avatar answered Sep 25 '22 15:09

Brian Cairns