Browse Source

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
pull/3260/head
Ike 2 years ago committed by GitHub
parent
commit
917c657439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      src/Identity/IdentityServer/BaseRequestValidator.cs
  2. 7
      src/Identity/IdentityServer/CustomTokenRequestValidator.cs
  3. 6
      src/Identity/IdentityServer/ResourceOwnerPasswordValidator.cs

24
src/Identity/IdentityServer/BaseRequestValidator.cs

@ -26,6 +26,7 @@ using Bit.Core.Utilities; @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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
{

7
src/Identity/IdentityServer/CustomTokenRequestValidator.cs

@ -14,6 +14,7 @@ using IdentityModel; @@ -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 @@ -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;
}

6
src/Identity/IdentityServer/ResourceOwnerPasswordValidator.cs

@ -13,6 +13,7 @@ using Bit.Core.Utilities; @@ -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 @@ -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;

Loading…
Cancel
Save