Search code examples
.netidentityserver4

How to revoke refresh tokens if a OneTimeOnly token is used multiple times in Identity Server 4


I am using Identity Server 4 + ASP.NET Core Identity and have a setup where refresh tokens are used to request an access token for the application. These refresh tokens are configured as OneTimeOnly tokens. This means every time you use a refresh token to retrieve a new access token, the refresh token should be discarded and you receive a new refresh token.

The default implementation of Identity Server simply rejects subsequent token requests with an already used refresh token.

For security reasons however, having a refresh token being used multiple times could mean the token has been leaked. Therefor I'd like to revoke all tokens for that user, forcing him/her to log in again.

The documentation says I need a custom implementation of the DefaultRefreshTokenService class' AcceptConsumedTokenAsync virtual method. But what exactly should I implement?


Solution

  • To remove all refresh tokens for a given user, it is indeed needed to override the AcceptConsumedTokenAsync virtual method. The DefaultRefreshTokenService class has a property RefreshTokenStore, which can be used to remove all tokens from the database:

        public class RevokingDefaultRefreshTokenService : DefaultRefreshTokenService
        {
            public RevokingDefaultRefreshTokenService(
                IRefreshTokenStore refreshTokenStore,
                IProfileService profile,
                ISystemClock clock,
                ILogger<DefaultRefreshTokenService> logger)
                :
                base(refreshTokenStore, profile, clock, logger)
            {
            }
    
            protected override async Task<bool> AcceptConsumedTokenAsync(RefreshToken refreshToken)
            {
                // Revoke all refresh tokens for this user
                await RefreshTokenStore.RemoveRefreshTokensAsync(refreshToken.SubjectId, refreshToken.ClientId);
    
                // Base impl
                return await base.AcceptConsumedTokenAsync(refreshToken);
            }
        }
    

    Do note, if the refresh token was leaked, the malicious user can still access your system for as long as the acces token he received from this refresh token is valid. Therefore, make sure your access token has a reasonable lifetime.