In the current implementation of doorkeeper, When the access_token is refreshed doorkeeper also sends a new refresh_token.This is a valid implementation but it becomes problematic when there are concurrent apis calls from my client side (ios, android) calling to refresh the access token at the same time. This means that there will be at least 1 thread ending up with expired tokens that it cant refresh.
Anyone has solution for this race condition?
We've solved this before (not with doorkeeper) a couple of different ways.
Request Queue:
On our mobile apps we've implemented a request queue, and just before a request is made we check if the token needs to be refreshed then we pause the queue, refresh the token, then unpause again. No changes to the server required in this case
This has tradeoffs (you need to sync your request threads etc), but is pretty reliable at stopping the refresh contention without needing to modify the server.
Refresh jitter and JWT:
Since we are using JWT (where the access_token expiry is written into the token and not revoked at the server end), you can add a random number of "jitter seconds" to the refresh expiry each time you check. This decreases the likelihood of two requests trying to refresh at the same time. I used this in an AngularJS app that would get all confused with several tabs open. There was a random chance one tab would refresh before the rest, while the rest could continue to use their existing access_token until the new one was returned and updated.
This would probably also work without JWT if you can manage to get your access_tokens to stay valid when their corresponding refresh token is used, which would allow the 'other' requests to continue to use their 'old' token until the next time.
It's not entirely foolproof, but reduced the likelihood enough we were happy with it.
Expiry buffers for tokens:
The last way was when a refresh was executed, don't actually expire the token for another few seconds later so any 'concurrent' threads just get the new token returned. This was easy enough when I'd written the server component from scratch, but might not be so easy with doorkeeper. I think you'd get more milage from the other two approaches.
Related
Im wondering if anyone has any answers to this. If one follows the quickstart here:
https://developers.google.com/calendar/api/quickstart/go
we can get authorization from a user that allows us to do things with their calendar.
It seems the HTTP Client given back from the config handles calling the refresh token and keeping the client up to date.
First, as a side-question, does anyone know how long this is allowed to persist? I read in a doc somewhere that for unpublished apps/projects its 7 days, but it didn't specify a timeline otherwise. Is it indefinite or will this expire at some point (ie the refresh token runs out after 30 days). And is this avoidable at all?
now, the real question - if you take the service down entirely, ie for an update, and restart it, is there any way to opick up the authorization again? In the tutorial its writing a code to disk and reading as needed. In my case I just hold reference to the things I need in the application, so obviously when the service went down id have to reauthorize. I then tried writing the code to disk on a persistent volume and re-reading from that, but it doesnt get authorized - I guess because the new instance of the service has a new instance of the oauth config, and it wont accept the old code perhaps?
In any case, im wondering if anyone knows of a way to make this persist through ssytem restarts, or if the only real option here is to move the calendar service client to a small microservice on the side that we do not restart, and contact it from elsewhere as needed.
Thanks in advance!
The refresh token expiration is documented here. As you've noted, tokens expire in 7 days for projects in "Testing" status. Refresh tokens for projects in production last indefinitely, but there are some scenarios where they may get invalidated (user revokes access, token has not been used in 6 months, the account has over 50 live tokens, etc.). It's not mentioned but if you change your OAuth scopes you'll probably need to reauthorize as well.
This means that as long as you keep your refresh tokens and none of the invalidation conditions are met, you should be able to keep using them to get new access tokens even if your service goes down temporarily. But as mentioned in the documentation, you should anticipate the possibility that the token stops working and redirect the user to authorize again.
My guess is that in your case the refresh token stopped working for some reason (maybe the 7 days limit) and the application just tried to use the same expired token. The Quickstarts in Google's documentation usually handle the creation of the token file and how to reuse it, but they don't include the part where you have an invalid token and need to reauthorize.
References:
Google's OAuth2 Overview
OAuth2 for Web Apps
I have one quick question related to "acquiring a new access token upon expiration". I have read some tutorials where people write code to manually request a new access token.
In my case I wrote an ASP.NET MVC app to access Google APIs, such as Gmail API, and I am using API Client Library for .NET for that.
After OAuth 2.0 authorization I get back the result object of type AuthorizationCodeWebApp.AuthResult.
Where result.Credential.Token contains AccessToken and RefreshToken properties.
I save the refresh token in my web.config the very first time when it comes back (after the consent screen). All next requests dont have a refresh token, only an access token that expires after 1 hour.
So, my question is - before I make a call to instantiate a Gmail Service, I assign previously saved refresh token:
result.Credential.Token.RefreshToken = WebConfigurationManager.AppSettings["RefreshToken"];
var service = new GmailService(
new BaseClientService.Initializer { HttpClientInitializer = credential });
When result.Credential.Token.AccessToken expires, does Gmail API (or any other API Client Library for .NET) acquires a new access token automatically if result.Credential.Token.RefreshToken was assigned a valid refresh token value previously saved, like in my code sample?
Thank you!
UPDATE - More clarification to my question With the same refresh token, how many times I can aquire a new access token when making calls to Google API?
I will explain: access token expires in 1 hour, right.
If I keep making calls with, lets say, 10 minutes intervals to Gmail API (for example), after 6 calls (1 hour limit), Gmail API will use my refresh token to acquire a new access token. After 6 more calls (1 more hour) the whole thing repeats itself. Question - is there a limit to it? Remember, I am not changing my refresh token. Same refresh token is being used to acquire a new access token. And for how long this repetitive calls may continue without any error?
UPDATE AFTER THE TEST
I let my application run on my local machine in Visual Studio DEBUG mode trying to catch any exception, NO Human interaction.
The application kept receiving AJAX calls to Gmail Action with 2 minutes interval, everything was working fine, I went to the gym, came back 2 hours later - oops, Visual Studio debug is open on this Token has been revoked exception, here we go, so it's clear the token was revoked by the Google API service, as you can see from the Debug window. The only question remains - why, since there are no specific details are provided, there is no Inner Exception just that general error message and no reason, but the source is clear - Google API, we can even see it came back from
Google.Apis.Requests.ClientServiceRequest`1.Execute() в
C:\Users\mdril\Documents\GitHub\google-api-dotnet-client\Src\GoogleApis\Apis\Requests\ClientServiceRequest.cs:row
96
I am guessing the service shuts down (revokes a token) after N number of calls, maybe within certain interval. If some one knows the limitations of Google API in terms of number of calls or time intervals between calls, please let me know.
It seems that Matthew Riley, the custodian of Google API on github, coded some logic to revoke a token based on some criteria: https://github.com/google/google-api-dotnet-client
Long response to comment :
One question though: can this be done indefinitely long, unlimited number of times, or I will get an error at some point?
Refresh tokens can be come invalid for the following reasons:
user can revoke it in there google account.
if a refresh token isn't used for 6 months to get a new access token it will expire automatically.
If a user authenticates your application you get a refresh token if they do it again you get a different refresh token. Both will work. you can do this up to 26 times. on the 27 th time the user Authenticates your application the first one you got will expire. You can only have 26 live refresh tokens. (DONT ASK how I know this! "#¤%&)
So assuming you don't reauthentcate your application to many times, use the refresh token at least once every six months. You can use it as many times as you want.
Update for comment:
I think you are still confused. Access tokens expire after 1 hour. Refresh tokens only expire for the above reasons you can use them as many times as you like. To get a new access token.
However you can only have 25 working refresh tokens.
Lets say I have a windows service application that backs up files to a users Google drive account. A user installs it on a server and authenticates it and gets a refresh token. Every night the windows service runs and backs up the files to google drive, it uses the refresh token to get a new access token.
Lets say this user really likes my auto super imba backup service. He installs it on another server. He gets another refresh token and the application goes about its business uploading files at night
Lets say my super user really has a server farm he installs my application on 25 servers. Those applications will be able to get new access tokens forever.
However if this crazy user installs it the 26 th time on a different server getting a new refresh token for this server. The first server they installed it on will stop working because google only allows you to have 25 outstanding refresh tokens for an application.
This is user application based so you can have any number of users each with a max of 25 refresh tokens
I've integrated with Gmail OAuth and have it working pretty good. The only remaining issue is using the refreshToken to retrieve a new accessToken when it expires. I persist both of these in CoreData so I have easy access to them anywhere in my app. My problem is that I don't know where to do the refresh. Does Mailcore provide an NSNotification or similar when a request fails? This way I know that the token needs refreshed and I can refresh it and retry the request with the new token.
Update:
My current solution is simply to refresh the token every time the app becomes active. I would prefer to only refresh the token when it expires (i.e. when a request fails).
Instead of randomly guessing when a good time to update the token would be, use the 'expires_in' value to run some kind of logic about whether or not enough time has passed to warrant a refresh of the token.Or What I do is grab the token the first time and set a timestamp. Then, on the next call to do anything IMAP/SMTP related I check if the current timestamp is >= 50 minutes since the last update (tokens are only good for an hour so this seems to work well).
Hope this helps! :)
My Box app (using v2 API) keeps track of when an access token expires, and when the app realizes the access token is about to expire, it request a new access token using the refresh token (the reason is that it simpler to avoid errors than to handle them).
I noticed that lately (this definitely was working properly a few months ago), when my app starts up (after more than 1 hour of non-use), the request for a new access token fails! The interesting thing is that at this time, my app has not attempted to use the now expired access token.
Should my app first do a dummy action with the expired access token maybe as a workaround? Again, all this was working as expected when I converted the app over to V2 API. At that time, the refresh token was valid for 14 days.
Thanks
Peter
I've tried to reproduce it, and I'm not able to. My refresh tokens are all working. It could be that you are getting an error condition on your refresh that you are not catching. There are some cases where your admin can decide that the app you are using is no longer approved for your enterprise, and on refresh, you'll be booted out. Or you may have mis-typed your password enough times that your password is going into "captcha" mode, looking to verify that there's a human, and not a machine on the other end of the wire.
Let me suggest that you log out of the app, and log back in. You're more likely to get presented with the error, since Box will be giving you the auth screen, and Box handles all the weird cases in their OAuth2 screens.
Yet another reason, that for all the pain of implementing Oauth2, it's worth it to get a better experience for your users.
I'm calling into a 3rd party API from my ASP.NET MVC app. This API uses a service/API token that you get by calling a "renew" API. It expires after 5 days.
If you call one of the service's APIs and the token as expired, you get an "api token expired" error and you can call the renew function.
I don't want to do this because it complicates my code by having to always check for the "api token expired" error and have the logic to retry. I'd rather just make sure the renew function gets called "relatively frequently but not on every request".
I do not want to use a timer or chron solution if I can avoid it.
I thought I read somewhere that IIS7+ automatically by default recycles the application instances on a regular basis. If this default is less than 5 days then I can just use Application_Start.
You can just use Application_Start and check that it has been at least x time since the last time you renewed the token, perhaps by storing a DateTime of when the last token was retrieved in a database or file.
You shouldn't have the app automatically starting up just for this.
(my advice would be to use the API in the way it is supposed to be used; you might run into a problem with the API provider if you request it too often, for example.)