I already wrote about the hardening of refresh tokens in this post. I would recommend reading this first.
The upcoming OAuth 2.1 spec is pretty clear about refresh token handling:
- If the client is confidential, the refresh token must be bound to the client via the client secret.
- If the client is public, the refresh token must be rotated.
We have always supported client-binding, rotation and also sliding expiration, but we made a couple of changes in v4 to make customization of refresh token handling easier.
First of all we consolidated all refresh token related code in a single extensible place – the IRefreshTokenService with a default implementation called DefaultRefreshTokenService.
Next, we don’t delete consumed refresh tokens anymore from the database. Instead we record the consumed time. This allows for auditing as well as custom handling of refresh token replays of rotated tokens.
The reason you need to be able to implement custom policies is, that receiving a rotated refresh token more than once, is not necessarily always an attack. Faulty client logic like race conditions could be a reason for that, also unstable network connectivity could be the cause.
Depending on your system you might want to tweak the behavior, e.g. implementing a grace period to accomodate network failures, or you might also want to take additional steps if you see a refresh token replay, e.g. revoking other tokens or send notifications.
All of this is now easily possible – check the updated docs.