Gitlab OAuth Refresh token expired - oauth-2.0

I have integrated a Git-Lab OAuth app in my web-app. Users authenticate this OAuth app and give access to their Git-Lab repositories through the Access Token. Once a user connects his repositories with my web-app , my web app saves the refresh token and access token in the DB and a cron job runs every 2 hours to refresh the tokens so that It never loses the connection to the connected repositories. (GitLab access token expires after 2 hours).
Here is the Git-Lab API URL which my cron job hits to refresh the tokens.
https://gitlab.com/oauth/token?client_id={}&client_secret={}&refresh_token={}&grant_type=refresh_token&redirect_uri={}
Now this cron job to refresh the tokens was running perfectly since more than a week, and suddenly it failed to refresh the tokens and hence I have lost the connection to user repositories. The error message received from GitLab API is as follows
The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.
This job was running fine since a week, so all provided parameters to the Git-Lab API URL seems fine to me. e.g. client_id, client_secret, refresh_token ,redirect_uri etc.
What can be the possible reason of these token expiration. I have lost the connection to user repositories and the only choice I am left with is to go back to user and ask them to reconnect their repositories by re authenticating the OAuth App.
Can it be the reason that my cron job was running too often (12 times a day ) and refreshing the tokens at a high frequency Or may be if the Access token was still valid and a try to refresh that token caused this issue. ?
No, I can hit the API to refresh token as many times as I want, I could hit it 100 times in few seconds and got my tokens refreshed 100 times.
I also used the access to token to pull a repository , then updated the tokens and again used the previous access token (expired) to pull the repository, this time it failed to pull the repository for obvious reason (token has expired) , and then I updated my tokens again. I can successfully update my tokens all the time.
That means using an expired token to pull the repository is not the reason for tokens expiration
I need to know the real cause which has expired my tokens.
I need to emphasize on that my Refresh Token has expired and I can no longer refresh my tokens.
I have read through the GitLab API docs and haven't found any clue of the reason why my token got expired.

It seems like you tried to use a token that has been refreshed. That is, between the time you pulled the token value and used it, the refresh occurred, which immediately invalidated the token you were trying to use.
my web app saves the refresh token and access token in the DB and a cron job runs every 2 hours
Don't do this. In general, you should not be periodically refreshing these tokens because you will invalidate the active token, nor is there any real reason you should need to do so.
Just refresh the token at the moment when you actually need to use them if the token is expired. Even though the access token may expire, the refresh token can still be used after the access token itself expires -- that's what the refresh token is for, after all.

Please check that your service waits enough time for a response from GitLab(may be reasonable to increase timeout) and doesn't have retry logic - the refresh token could be used only once.

Depending on the resource owner (Gitlab, etc.), there are various reasons why a refresh_token could have become invalidated.
A few to note:
Your integration was reauthorized, invalidating any existing access_tokens and refresh_tokens
If a new access_token is generated, both the previous access_token and refresh_token will become invalid.
If the integration was deauthorized by an end user or token revoked programmatically (not sure if Gitlab offers this)
You might also find some of the tips in this OAuth Troubleshooting Guide helpful.

Related

How to create additional OAuth2 Access Tokens without user interaction

I have a web service that makes use of the Authorization Code grant type to get an Access Token for user data on a Resource Server, together with a Refresh Token. Now my web service launches jobs at certain points in time that should also access this user data. However,
there may be more than one job running at a time,
the user is not necessarily around when the jobs are spawned, and
the jobs may be long-running, in particular they may live longer than the validity of the Access Token.
My question is: How can I provide each job with an Access Token so that
no user interaction is required for every single job launch, and
each job can refresh its own Access Token if necessary, without invalidating other jobs' tokens?
While https://datatracker.ietf.org/doc/html/rfc6749#section-1.5 states that a refresh token can be used to obtain "additional access tokens" (emphasis mine), the spec is not so clear on whether the previous Access Token remains valid until it's expiry date (well, if it does not I wouldn't call it "additional"). Also, according to https://datatracker.ietf.org/doc/html/rfc6749#section-6 if a server issues a new Refresh Token to prevent replay attacks, then the old Refresh Token cannot be used any more, so now maybe I have an additional Access Token, but I can't really pass the Access Token and the new Refresh Token to the job, because if the job made use of that Refresh Token would then my web service couldn't use it any more.
Are there maybe any lesser-known flows that allow for this kind of unattended Access Token creation?
Normally access tokens and refresh tokens are valid till the exipration time.
Having multiple access and refresh tokens are also allowed.
However a refresh token can be revoked under following scenarios
the authorization server has revoked the refresh token
the user has revoked their consent for authorization
the refresh token has expired
the authentication policy for the resource has changed
Since you are having the background jobs running. I suggest not to use JWT Tokens for authentication. Instead you can have your custom security standards.
Like custom API Key, UserAgent for Jobs and you can pass the user information with the requset or as custom-header as well.

Oauth2: best practice to keep access_token fresh?

I'm creating an app that integrates with several 3rd-party Oauth2 providers (think Zapier). The user adds a connection through Oauth2 which gives me a refresh token and access token and which I'm storing in a database.
What is the best practice to keep the access token fresh? Should I be running an async job (e.g. cron) that refreshes the access token every 30 minutes for every connection? If the user doesn't use my app for several days I'd like to still be able to access his 3rd-party data without having him to go through the Oauth2 consent.
What is the best practice to keep the access token fresh? Should I be running an async job (e.g. cron) that refreshes the access token every 30 minutes for every connection?
Not necessarily. Wait till your API call fails. Check for a proper response such as "401 Unauthorized" which hints your access token is invalidated/expired. Once this happens use refresh token to renew the access token. If refresh token fails, then you have to fall back again and ask user to login again.
A refresh token can have a varying life time. It can be from few days to few months. For example check Google's explanation mentioning long lived refresh tokens and possible expiry of them. Also check how Azure AD mention about configurations related to token lifetimes.
So user not using app for few days (ex:- leave it and return after weekend) can be handled through proper validity configurations of tokens lifetimes. But be mindful about threats that could occur from long-lived, unmanaged tokens (ex:- due to token stealing).
The Oauth website has a pretty informative answer
The “expires_in” value is the number of seconds that the access token
will be valid. It’s up to the service you’re using to decide how long
access tokens will be valid, and may depend on the application or the
organization’s own policies. You could use this timestamp to
preemptively refresh your access tokens instead of waiting for a
request with an expired token to fail. Some people like to get a new
access token shortly before the current one will expire in order to
save an HTTP request of an API call failing. While that is a perfectly
fine optimization, it doesn’t stop you from still needing to handle
the case where an API call fails if an access token expires before the
expected time. Access tokens can expire for many reasons, such as the
user revoking an app, or if the authorization server expires all
tokens when a user changes their password.
If you make an API request and the token has expired already, you’ll
get back a response indicating as such. You can check for this
specific error message, and then refresh the token and try the request
again.
TLDR: I would only refresh the token when a request fails

How do I request a new refresh token when the last one expired?

I have two accounts for docusign, live and demo. The demo one hasn't been used in a while, in fact the last time a refresh token was created was the 14th November. I'm therefore assuming it has expired and is potentially stopping me from getting a new one.
When I make a request I get the invalid_grant error. The same code (with testing credentials) works fine with my live account which requests a live token every couple of days or so. It also did work fine on with the testing credentials until it wasn't used for a prolonged period.
I assumed I need to get hold of a new refresh token but without a valid previous token I'm not sure how to go about this.
The following applies to DocuSign's OAuth2 authentication service:
When you use the refresh token to get a new access token, you also get a new refresh token. But see the following:
If your original OAuth request only included the signature scope then the expiration date of the new refresh token will be the same as the original refresh token (30 days).
However, if you request both the signature and extended scopes, then your new refresh token will expire 30 days from the time that you refreshed it.
So the way you can continue to get a new access token without requiring the user to authenticate again is:
User authenticates with the signature and extended scopes. Your app exchanges the auth code for an access token (good for 8 hours) and a refresh token (good for 30 days).
Within the 30 day period, refresh the access token. This also gives you a new refresh token, good for a new 30 day period.
Rinse and repeat. As long as you get a new refresh token at least every 30 days, you can keep going forever.
Caveat: For InfoSec reasons, the end user, their admin, and/or DocuSign might invalidate all existing refresh tokens. This is an unusual corner case but can happen. Easiest way to test the corner case: remove the user's consent for the app.
You cannot refresh a Refresh Token if the Refresh Token has expired or otherwise been revoked. You must repeat the authentication flow to obtain a new Refresh Token.

Azure AD login session time out

I have implemented openid connect authentication using azure active directory in my website. The session expires every 1 hour. So the user is logged out, and redirected back to the login page. While analyzing based on it, i have found a solution in the below link, to use UseTokenLifetime = false
https://github.com/aspnet/Security/issues/147
Will this fix my issue? or is there any chance of increasing the session time?
Thanks in advance
Dinesh
Read here to learn more about Configurable Token Lifetime
In general, access tokens have a very short lifetime. This is intentional. Your web application should be using the refresh token which comes with your access token to acquire new access tokens once the original one expires.
When you get an access token and refresh token (assuming all default settings), the access token will last up to one hour. The refresh token will allow you to get a new access token + refresh token pair for up to 14 days. The new refresh token you get will last another 14 days, allowing you to chain new access tokens up to a total of 90 days, where you will need to eventually ask the user to sign in again.
You have the ability to configure token lifetimes such that your "refresh token chain" will never expire, however any individual access token or refresh token will eventually expire.

Getting "invalid_grant" error when exchanging a refresh token for an access token

I've seen this issue in a lot of questions, but so far, none seem to apply to my situation.
The problem we are having is we are getting an "invalid_grant" error when we attempt to get an access token. This only happens to some accounts, but when it does happen, in every case I looked at, the refresh token worked before, and now has stopped working. This is happening far to frequently for it to be customers revoking access (seems to be nearly 20% of the channels we manage in the last couple weeks have been invalidated).
As a note, we have a backend process that uploads the videos to our customer's YouTube channels.
We use OAuth2 to get a refresh token, here are the parameters we send...
scope = "https://www.googleapis.com/auth/youtube",
client_id = "",
response_type = "code",
access_type = "offline",
approval_prompt = "force",
redirect_uri = "http://www.us.com/OAuth/YouTube"
NOTE: for client_id we use the email address that is in the Google API manager (or was, I just looked and it is no longer there). We used to use the client ID from this page, but that caused us problems as well. Did this change? Should we be using the client ID from this page now?
We exchange the code that is returned for a refresh and access token and store the refresh token in our database.
The backend process exchanges the refresh token for an access token and this is where we seem to be getting the "invalid_grant" error.
Guaranteed only a single access token for the channel is in use at any time (25 limit doesn't apply). We don't store the access token, we get a fresh one every time we process a channel.
Any ideas what might be happening? Something to look for? See note above about client ID. This might have something to do with it, but I'm hesitant to try it since using the "Client ID" from the API manager caused problems before.
Guaranteed only a single access token for the channel is in use at any time (25 limit doesn't apply). We don't store the access token, we get a fresh one every time we process a channel.
This statement is incorrect: Access tokens can be used as many times as you need while they are still good (for an hour).
Answer:
"invalid_grant" basically means that your refresh token no longer works. The only solution to the problem is to request access again and get a new one. The question should be why is it expiring in the first place.
Assuming that the user did not revoke access, and that the refresh token has been used to request a new access token within the last six months. This is probably an issue with it being over written.
When a user authenticates your application you are given a refresh token. This refresh token is associated to the client id of your application and the user who has just authenticated. If said user then authenticates your application again you will get another refresh token. Again this refresh token is associated to the user and your projects client id. Both of these refresh tokens will work. Your user can keep doing this up to 25 (Note I think the changed it recently to 50 but I haven't tested it with all APIs yet) once they have hit this magic number the first refresh token will expired and if you try and use it you will get an invalid grant.
The only solution is then to just request authentication again. It is important to always save the most recent refresh token that your user has granted your application. In the event (like me) you have an application that is stored on a number of servers all requiring authentication. Your going to have to tell them not to refresh it to many times or they will have to go back and reauthenticate the first one that they expired.
If this is happening with ALL of your requests. You can also check that you server is sync with (NTP) and that you are sending the payload of your request in the post field. Not attached to the authentication end point like a HTTP GET (been there done that).
Here are the possible reasons why a token stops working and becomes invalid:
The user has revoked access.
The token has not been used for six months.
The user changed passwords and the token contains Gmail scopes.
The user account has exceeded a certain number of token requests.
As you can see, it's not recommended that you request a fresh one every time you process a channel. As also mentioned in Token expiration:
If you need to authorize multiple programs, machines, or devices, one workaround is to limit the number of clients that you authorize per user account to 15 or 20. If you are a Google Apps admin, you can create additional admin users and use them to authorize some of the clients.
With regards to the use of client_ID, it is usually needed to call the sign-in API as mentioned in Creating a Google API Console project and client ID.
And lastly, this Google Groups discussion - OAuth 2.0 400 - error:invalid_grant and ideas? might also help.

Resources