Say I'm developing an Evernote or YouTube client, and after the user logs in, the app gets an authorization token from the API server.
Then I use this token to interact with the server until, sometime later, the token expires.
The key is, I don't know if this token is expired until the server returns an error with a message like 'token expired'. Then I have to fetch a new token. Maybe it occurs while the user is posting a message.
So what is an elegant way to deal with this scenario? I want to combine fetching a new token with continuing the last request, so that the user just feels it works as usual.
It depends on which flow you're using. But in general, if you are able to refresh the auth token (via refresh token) without redirecting the user for credentials again, you should do it seamlessly.
Also, you should know exactly when a token is going to expire. If you're coding against an OAuth 2 implementation, the auth token response should give you an 'expires_in' field as a time span telling you exactly how long the token will expire after it was issued to you.
Related
In my web application I am using access and refresh tokens to authorize user access to protected resources, the flow is as following:
The user, through a mobile app, sends a request to the "auth/token" endpoint providing his credentials. The server authenticates the user and issue an access and refresh token. The refresh token is saved in a whitelist to be able to revoke it later on, if necessary.
Upon access token expiration, the mobile app sends the refresh token to the "/token/refresh" endpoint presenting the refresh token. A new access/refresh token pair is created and the old refresh token is invalidated ,implementing in this way the token rotation.
Now the problem:
Let's say that the client refresh the token but never receive a response back from the server because the network is lost. After 30 mins the client tries to refresh again but its token is now invalid and the user is logged out. In the Oauth implementations we can set a time for the old token after which this will be invalidated, giving the possibility to the mobile app to resend the same refresh token if any problem occurs. But I do not think this is a solution because we can't be sure of when the mobile client will retry to refresh the token. It can be in some minutes, hours or worse also days.
How do you approach this problem?
The first solution I could think of is increasing expiration time.
I see most of people saying when we use refresh token to exchange for a new access token, the auth server would issue a new refresh token and invalid the previous one. Refer
OAuth Refresh Token Best Practice
But from the OAuth website
https://www.oauth.com/oauth2-servers/access-tokens/refreshing-access-tokens/
It says the auth server can "optionally issue a new fresh token in response, or if we don't include a new refresh token, the client assumes the current refresh token will continue to be valid"
So, it looks like both options (keep or renew refresh token) are acceptable to OAuth2 standard.
My questions are:
1) Do both options are equally secure?
2) If the auth server returns a new refresh token but the client fails to receive (e.g. network error), the client has no way to re-gain access token with existing refresh token, which already invalidated. Correct?
3) If the refresh token has been leaked to someone else, both the attacker and the victim client can use it. If the auth server takes the renewal approach, then only the first one to use the refresh token can re-gain access token. So, if the victim found the refresh token is no longer valid, it may think that the refresh token has been compromised. Is this the reason for the "renewal approach"?
2.) Yes, that's correct.
3.) That's correct too. You can take a look at the OAuth 2.0 for Browser-Based Apps RFC which discusses the refresh token regeneration. It's important mainly for public clients - the ones without client_secret, since a refresh token can be exchanged for an access token right away.
1.) Refresh token regeneration is a security feature - it shortens validity of a stolen refresh token and it enables the auth server to detect that refresh token had been compromised. So it's more secure to use it than not. But it may be more convenient for private clients not to get a new refresh token on each use - for example to prevent the refresh token loss due to network error - as you described it in point #2.
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
On the rfc https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-12#page-40 , you can see a sentence saying "Removed 'expired_token' error code."
That is, "invalid_token" will be used for expired token cases.
Why was that? I think it was very useful for the client to distinguish an expired token and an invalid token. Without "expired_token", how can the client decide whether to refresh an access token?
It was removed to prevent potential security leaks of information that attackers might use.
Let's say an attacker can get a copy of your Access Token by sniffing all of your network traffic. But the attacker only analyzes this information after your token has expired. If the attacker tries to make requests to your service with that token, and receives back an 'expired_token' code, they know that they are onto the right thing, and just need to get a later copy.
If the attacker, however, receives back an 'invalid_token' message, it makes it more difficult for the attacker to understand that the data they had WAS an access token (maybe it was something else?).
Furthermore, the way access / refresh tokens typically work, is something like this:
A client needs to make an API request and has an access token and a refresh token.
The client makes a request to the API service with their access token and gets an 'invalid_token' message.
The client locally validates their refresh token, sees it is still valid.
The client then submits a refresh request using their refresh token, and gets a new access token.
If the client locally validates their fresh token above, and gets an error (maybe the token expired, or something), they know they need to re-authenticate the user FRESH to receive both a new refresh AND access token.
The same is true if, when the user submits their refresh request, their refresh token is considered invalid (maybe the server-side revoked this token?).
I'm developing a API to be used with my IOS App and I am curious on the best practice for using the refresh token in oAuth2. I am using the user password grant to generate a access token and refresh token.
If the token expires every 60 minutes then that means every 60 minutes the client will have to make 3 consecutive API calls: 1. use the access token to get the resource from the API, 2. api responds with a invalid token so we need to use the refresh token, 3. now that the token is refreshed we need to try the initial call again.
So, what I am wanting to know is if it's best to refresh the token just before it expires? Or is it better to generate a new access token once the API has responded with a expired token error?
Not sure there's a best practice as such, but it's certainly more efficient to refresh in advance as you then won't make requests that you can know will fail. The cost of the timer to refresh is much less than the cost of the network communication.
You still need handling for the token being invalid on any request as the server could invalidate the token for any number of other reasons, so this is really a 'how can I make this efficient and user friendly' than a 'can I remove some of my code' kind of thing. Handling the error response is the standard, refreshing in advance is the user friendly.
Be cautious about refreshing while you have in-flight requests and you could inadvertently cause a request to fail by invalidating its token...