From bc9ed71ff8d686f07f1e5a41d11925daa64d00d8 Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Sat, 31 Aug 2024 11:22:10 +0200 Subject: [PATCH] Added support for drifts in JWT refresh tokens. This can happen if the user refreshes the browser during the login. --- Duplicati/WebserverCore/Middlewares/JWTProvider.cs | 1 + Duplicati/WebserverCore/Services/LoginProvider.cs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Duplicati/WebserverCore/Middlewares/JWTProvider.cs b/Duplicati/WebserverCore/Middlewares/JWTProvider.cs index 789abe6e2..50f0d56b9 100644 --- a/Duplicati/WebserverCore/Middlewares/JWTProvider.cs +++ b/Duplicati/WebserverCore/Middlewares/JWTProvider.cs @@ -17,6 +17,7 @@ public record JWTConfig public int RefreshTokenDurationInMinutes { get; init; } = 60 * 24 * 30; public int SigninTokenDurationInMinutes { get; init; } = 5; public int SingleOperationTokenDurationInMinutes { get; init; } = 1; + public int MaxRefreshTokenDrift { get; init; } = 1; public SymmetricSecurityKey SymmetricSecurityKey() => new(Encoding.UTF8.GetBytes(SigningKey)); public static JWTConfig Create() => new() diff --git a/Duplicati/WebserverCore/Services/LoginProvider.cs b/Duplicati/WebserverCore/Services/LoginProvider.cs index e19fe4fae..a60446bac 100644 --- a/Duplicati/WebserverCore/Services/LoginProvider.cs +++ b/Duplicati/WebserverCore/Services/LoginProvider.cs @@ -1,10 +1,11 @@ using Duplicati.Library.Logging; using Duplicati.Server.Database; using Duplicati.WebserverCore.Abstractions; +using Duplicati.WebserverCore.Middlewares; namespace Duplicati.WebserverCore.Services; -public class LoginProvider(ITokenFamilyStore repo, IJWTTokenProvider tokenProvider, Connection connection) : ILoginProvider +public class LoginProvider(ITokenFamilyStore repo, IJWTTokenProvider tokenProvider, JWTConfig jwtConfig, Connection connection) : ILoginProvider { private static readonly string LOGTAG = Log.LogTagFromType(); @@ -29,7 +30,8 @@ public class LoginProvider(ITokenFamilyStore repo, IJWTTokenProvider tokenProvid var tokenFamily = await repo.GetTokenFamily(refreshToken.UserId, refreshToken.TokenFamilyId, ct) ?? throw new UnauthorizedAccessException("Invalid refresh token"); - if (tokenFamily.Counter != refreshToken.Counter) + var counterDiff = tokenFamily.Counter - refreshToken.Counter; + if (counterDiff < 0 || counterDiff > jwtConfig.MaxRefreshTokenDrift) { Log.WriteWarningMessage(LOGTAG, "TokenFamilyReuse", null, $"Invalid refresh token counter: {tokenFamily.Counter} != {refreshToken.Counter}"); await repo.InvalidateTokenFamily(tokenFamily.UserId, tokenFamily.Id, ct);