-
Notifications
You must be signed in to change notification settings - Fork 193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FR: callback to catch token exceptions #155
Comments
Before I start diving into the code and doing PRs and suchlike, I just wanted to check if I am missing something with perhaps other workaround, or if other people also have this [potential] problem. |
Thank you for reporting this issue! We would love to see a PR to fix this. Another way we could do this is we could be conservative when checking to see if the access token is expired, and give ourselves a few seconds of wiggleroom, e.g.: $wiggleroom = 5; // 5 seconds
$isExpired = time() + $wiggleRoom < $expires; |
I haven't looked at this for a while, so I'll try the latest version and have a dig around again. I don't have any qualitative evidence, but I would think the wiggle room would help some cases where the network happens to be a bit slow. It is just renewing a few seconds early, which can't be a bad thing. I'm using a longer wiggle room because I have some longer running scheduled jobs. I'm guessing long running jobs would be discouraged on cloud-based services where each job is there to do something simple and finish quickly. |
I think the main issue here is the library cache by default does not actually look at the token expiration. It just calls for a new access token on every request and caches the fetched access token in memory. One solution is to store the full access token in the cache, and check the expiration when the access token is retrieved (or set the cache's expiration to the token expiration. either way should work as long as the expiration is valid). This would allow users caching across multiple requests to make a minimum number of token requests to the APIs. |
Let me take a step back and explain how I think this works, where I think - as an application developer - it can make things difficult. If my understanding is incorrect, please say:
On point 4, I have seen applications that do not use the callback, and retain the original access token without ever updating it. They end up renewing the token on every page load, once past the first 30 minutes, which can't be desirable. I realise that is a problem in the application development, but I wonder if it just needs to be highlighted more in the documentation? My use-case for all this is an application that runs on a user's own server or site, that the user authenticates, but then accesses the Google services on its own using scheduled jobs. I would like to be sure expired tokens (that may expire before the 30 minutes, or where the 30 minute timer was started significantly later than Google's own timer for whatever reason) are handled smoothly and preferably invisibly, and that the application is always informed about the new access tokens to help reduce hits on your services. Hopefully that clarifies my thinking and isn't just me repeating myself :-) |
This all sounds accurate, thank you for taking the time to type this out. Although I'm not sure about point 2. When are the previous tokens used to initialize the library? I assume this is happening in the application layer. We could definitely include in the Middleware (or add a new middleware) the logic to retry the authentication call if the request throws an expired exception. What do you think of the following changes?
I am seeing another problem with the caching, which is that |
On point 2, the initialisations, I meant the authentication client. The "library" was a little too broad. $client = new Google_Client();
// set scope, redirect URL, access type, approval prompt etc.
// Set the current access token:
$client->setAccessToken($json_token); The {
"access_token":"ya29.Gl3UBA86bj2DW...very-long...yPLmq1vpffV4",
"token_type":"Bearer",
"expires_in":3480,
"refresh_token":"1\/VRdVb3...Ixs",
"created":1506594548
} (The The current access token, its expiry and the refresh token are all in there. These details were stored in the application and are all that is needed to access Google services for an authorised user. These are the "initialisation details" that I referred to. Then here also the https://github.com/academe/laravel-google-api-auth/blob/master/src/Helper.php#L56-L83
I've not got a full grasp of how "middleware" is defined in this library, but if it is a layer to invisibly catch certain states and act on that, it is probably what we would be looking for. I think the point is that it would need to be caught right where the required API request is sent to Google, so that once the access token is renewed, it has all the information is needs to re-send the same request. So I guess middleware that handles this http message and its response would be the place? But I'm a little out of my depth at this point on implementation, but your suggestions sound good. I'll explore the middleware a little deeper to try to understand it more. |
When the client deems a token to have expired, using its locally stored creation time and expires_in values, it will automatically renew the token. The renewed token can then be stored/persisted by the application using the callback provided in #110 That works great, and takes the renewal burden away from the business logic of the application.
Now, the locally stored expiry time may be out of step from the real expiry time. This is especially the case at the edge of the token lifetime where HTTP delays in the fetching of the original token may put he expiry time out by one second or more, leaving the application thinking the token has a second more of life, but Google insisting the expired a second ago. This results in an exception, right in the middle of the business logic using the API.
This exception can be caught, the message checked, a token renewal attempted etc and that can get things back on track. However, that is not a domain that should impinge on the business side of the application; it really does not care about the OAuth negotiating wranglings, and it really should not have to care.
So, what I would like to propose for discussion, is a callback that can be used in a similar way to the expired token callback, to allow the application to invisibly attempt to fix any token issues in the event of an invalid token being found. I guess this callback would replace the exception once, then the client would reattempt the remote API call. If it works, great, all carries on happily. If it does not work, then fine, the application had a chance to fix its tokens but couldn't.
Issues I see are the recursive nature of this callback: the callback to the client trying to access the API would need to use the same client to renew the tokens. That may or may not have side-effects.
My workaround at the moment, to try to steer away from this edge-case, is to knock five minutes off the expires_in that the client supplies for a new token, so it renews early. But that's not ideal. This is all in the interest of keeping the lights on in offline access to the API when OAuth authenticated by an end user.
The text was updated successfully, but these errors were encountered: