Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 9 additions & 28 deletions src/Identity/IdentityServer/BaseRequestValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
using Bit.Identity.Utilities;
using IdentityServer4.Validation;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;

namespace Bit.Identity.IdentityServer;

Expand All @@ -46,8 +45,6 @@ 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; }
Expand All @@ -72,8 +69,7 @@ public BaseRequestValidator(
IPolicyService policyService,
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
IFeatureService featureService,
ISsoConfigRepository ssoConfigRepository,
IDistributedCache distributedCache)
ISsoConfigRepository ssoConfigRepository)
{
_userManager = userManager;
_deviceRepository = deviceRepository;
Expand All @@ -93,14 +89,6 @@ public BaseRequestValidator(
_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,
Expand Down Expand Up @@ -147,25 +135,18 @@ protected async Task ValidateAsync(T context, ValidatedTokenRequest request,
var verified = await VerifyTwoFactor(user, twoFactorOrganization,
twoFactorProviderType, twoFactorToken);

var cacheKey = "TOTP_" + user.Email + "_" + twoFactorToken;

var isOtpCached = Core.Utilities.DistributedCacheExtensions.TryGetValue(_distributedCache, cacheKey, out string _);
if (!verified || isBot || isOtpCached)
if ((!verified || isBot) && twoFactorProviderType != TwoFactorProviderType.Remember)
{
if (twoFactorProviderType != TwoFactorProviderType.Remember)
{
await UpdateFailedAuthDetailsAsync(user, true, !validatorContext.KnownDevice);
await BuildErrorResultAsync("Two-step token is invalid. Try again.", true, context, user);
}
else if (twoFactorProviderType == TwoFactorProviderType.Remember)
{
await BuildTwoFactorResultAsync(user, twoFactorOrganization, context);
}
await UpdateFailedAuthDetailsAsync(user, true, !validatorContext.KnownDevice);
await BuildErrorResultAsync("Two-step token is invalid. Try again.", true, context, user);
return;
}
if (twoFactorProviderType != TwoFactorProviderType.Remember)
else if ((!verified || isBot) && twoFactorProviderType == TwoFactorProviderType.Remember)
{
await Core.Utilities.DistributedCacheExtensions.SetAsync(_distributedCache, cacheKey, twoFactorToken, _cacheEntryOptions);
// Delay for brute force.
await Task.Delay(2000);
await BuildTwoFactorResultAsync(user, twoFactorOrganization, context);
return;
}
}
else
Expand Down
7 changes: 2 additions & 5 deletions src/Identity/IdentityServer/CustomTokenRequestValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using IdentityServer4.Extensions;
using IdentityServer4.Validation;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;

#nullable enable

Expand Down Expand Up @@ -43,13 +42,11 @@ public CustomTokenRequestValidator(
IUserRepository userRepository,
IPolicyService policyService,
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
IFeatureService featureService,
IDistributedCache distributedCache)
IFeatureService featureService)
: base(userManager, deviceRepository, deviceService, userService, eventService,
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
applicationCacheService, mailService, logger, currentContext, globalSettings,
userRepository, policyService, tokenDataFactory, featureService, ssoConfigRepository,
distributedCache)
userRepository, policyService, tokenDataFactory, featureService, ssoConfigRepository)
{
_userManager = userManager;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
using IdentityServer4.Models;
using IdentityServer4.Validation;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;

namespace Bit.Identity.IdentityServer;

Expand Down Expand Up @@ -45,12 +44,11 @@ public ResourceOwnerPasswordValidator(
IPolicyService policyService,
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
IFeatureService featureService,
ISsoConfigRepository ssoConfigRepository,
IDistributedCache distributedCache)
ISsoConfigRepository ssoConfigRepository)
: base(userManager, deviceRepository, deviceService, userService, eventService,
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
applicationCacheService, mailService, logger, currentContext, globalSettings, userRepository, policyService,
tokenDataFactory, featureService, ssoConfigRepository, distributedCache)
tokenDataFactory, featureService, ssoConfigRepository)
{
_userManager = userManager;
_userService = userService;
Expand Down