1
0
mirror of https://github.com/bitwarden/server.git synced 2024-12-23 17:07:42 +01:00

PM-2128 Enforce one time use of TOTP (#3152)

* enforcing one time MFA token use

* Updated cache TTL

* renamed the cache

* removed IP limit, added comment, updated cache Key

* fixed build errors
This commit is contained in:
Ike 2023-09-09 14:35:08 -07:00 committed by GitHub
parent 4b482f0a34
commit 917c657439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 5 deletions

View File

@ -26,6 +26,7 @@ using Bit.Core.Utilities;
using Bit.Identity.Utilities;
using IdentityServer4.Validation;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;
namespace Bit.Identity.IdentityServer;
@ -45,6 +46,8 @@ public abstract class BaseRequestValidator<T> where T : class
private readonly GlobalSettings _globalSettings;
private readonly IUserRepository _userRepository;
private readonly IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> _tokenDataFactory;
private readonly IDistributedCache _distributedCache;
private readonly DistributedCacheEntryOptions _cacheEntryOptions;
protected ICurrentContext CurrentContext { get; }
protected IPolicyService PolicyService { get; }
@ -69,7 +72,8 @@ public abstract class BaseRequestValidator<T> where T : class
IPolicyService policyService,
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
IFeatureService featureService,
ISsoConfigRepository ssoConfigRepository)
ISsoConfigRepository ssoConfigRepository,
IDistributedCache distributedCache)
{
_userManager = userManager;
_deviceRepository = deviceRepository;
@ -89,6 +93,14 @@ public abstract class BaseRequestValidator<T> where T : class
_tokenDataFactory = tokenDataFactory;
FeatureService = featureService;
SsoConfigRepository = ssoConfigRepository;
_distributedCache = distributedCache;
_cacheEntryOptions = new DistributedCacheEntryOptions
{
// This sets the time an item is cached to 15 minutes. This value is hard coded
// to 15 because to it covers all time-out windows for both Authenticators and
// Email TOTP.
AbsoluteExpirationRelativeToNow = new TimeSpan(0, 15, 0)
};
}
protected async Task ValidateAsync(T context, ValidatedTokenRequest request,
@ -135,6 +147,15 @@ public abstract class BaseRequestValidator<T> where T : class
var verified = await VerifyTwoFactor(user, twoFactorOrganization,
twoFactorProviderType, twoFactorToken);
var cacheKey = "TOTP_" + user.Email;
var isOtpCached = Core.Utilities.DistributedCacheExtensions.TryGetValue(_distributedCache, cacheKey, out string _);
if (isOtpCached)
{
await BuildErrorResultAsync("Two-step token is invalid. Try again.", true, context, user);
return;
}
if ((!verified || isBot) && twoFactorProviderType != TwoFactorProviderType.Remember)
{
await UpdateFailedAuthDetailsAsync(user, true, !validatorContext.KnownDevice);
@ -148,6 +169,7 @@ public abstract class BaseRequestValidator<T> where T : class
await BuildTwoFactorResultAsync(user, twoFactorOrganization, context);
return;
}
await Core.Utilities.DistributedCacheExtensions.SetAsync(_distributedCache, cacheKey, twoFactorToken, _cacheEntryOptions);
}
else
{

View File

@ -14,6 +14,7 @@ using IdentityModel;
using IdentityServer4.Extensions;
using IdentityServer4.Validation;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;
#nullable enable
@ -42,11 +43,13 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
IUserRepository userRepository,
IPolicyService policyService,
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
IFeatureService featureService)
IFeatureService featureService,
IDistributedCache distributedCache)
: base(userManager, deviceRepository, deviceService, userService, eventService,
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
applicationCacheService, mailService, logger, currentContext, globalSettings,
userRepository, policyService, tokenDataFactory, featureService, ssoConfigRepository)
userRepository, policyService, tokenDataFactory, featureService, ssoConfigRepository,
distributedCache)
{
_userManager = userManager;
}

View File

@ -13,6 +13,7 @@ using Bit.Core.Utilities;
using IdentityServer4.Models;
using IdentityServer4.Validation;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;
namespace Bit.Identity.IdentityServer;
@ -44,11 +45,12 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
IPolicyService policyService,
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
IFeatureService featureService,
ISsoConfigRepository ssoConfigRepository)
ISsoConfigRepository ssoConfigRepository,
IDistributedCache distributedCache)
: base(userManager, deviceRepository, deviceService, userService, eventService,
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
applicationCacheService, mailService, logger, currentContext, globalSettings, userRepository, policyService,
tokenDataFactory, featureService, ssoConfigRepository)
tokenDataFactory, featureService, ssoConfigRepository, distributedCache)
{
_userManager = userManager;
_userService = userService;