I have an OAuth2 server built with django-oauth-toolkit, and by default, refresh tokens are revoked immediately upon use. This means that if a client requests a new access token using a refresh token but doesn't receive the response due to a network interruption they will be forced to reauthenticate.
The library provides the setting REFRESH_TOKEN_GRACE_PERIOD_SECONDS
which is an amount of time to wait between the use of a refresh token and its revocation. If a client uses a refresh token and does not receive the response, that original refresh token will still be valid for REFRESH_TOKEN_GRACE_PERIOD_SECONDS
which allows the client to get a new access token without needing to reauthenticate.
As far as I can tell, the purpose of immediately revoking refresh tokens upon use is to prevent replay attacks, but since this authorization server exclusively uses https, it seems this is a sufficient defense against this type of attack.
Are there other vulnerabilities that can result from having a grace period for refresh token revocation? What would be the implications of never revoking a refresh token?
I wrote an article about the problems this grace period is intended to solve: https://danmercer.net/p/refresh-token-false-positives
Like Kyle said in his answer, rotating refresh tokens enables the authorization server to detect stolen refresh tokens, because a token will be used twice. The AS can then revoke all tokens derived from that refresh token, which stops the thief at the cost of requiring the end user to grant access again.
Unfortunately, this "breach detection" can cause problems for legitimate clients, if a client tries to use a single refresh token multiple times. For example, this might happen due to a race condition (two threads in the client try to get a fresh access token at the same time) or a network error (the refresh token response fails to reach the client).
To help mitigate those problems while retaining most of the breach detection benefit, some authorization servers (including django-oauth-toolkit, apparently) apply a small "grace period" before revoking the used refresh token. This allows any concurrency or retries in the client to "settle" and get the new fresh tokens without triggering the breach detection.
Now to answer your main question: a small grace period does weaken the breach detection, but only somewhat. A malicious actor would have to guess when the true client is going to refresh the token. And even if they guess successfully, as long as you give both of them (the malicious actor and the true client) the same new tokens, then the malicious actor will have to make another lucky guess the next time the client refreshes its tokens.
Although this idea isn’t formalized in the spec yet, several authorization servers support some kind of grace period on refresh tokens, including Auth0, Okta, Fitbit, Slack, and Lucid. I’ve also found it in Fauna’s auth “blueprints” code.