mirror of
https://github.com/bitwarden/server.git
synced 2025-07-05 18:12:48 -05:00
[PM-9925] Tokenable for User Verification on Two Factor Authenticator settings (#4558)
* initial changes * Fixing some bits * fixing issue when feature flag is `false`; also names; * consume OTP on read if FF true * comment typo * fix formatting * check access code first to not consume token * add docs * revert checking access code first * update error messages * remove line number from comment --------- Co-authored-by: Jake Fink <jfink@bitwarden.com>
This commit is contained in:
@ -0,0 +1,62 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Tokens;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Auth.Models.Business.Tokenables;
|
||||
|
||||
/// <summary>
|
||||
/// A tokenable object that gives a user the ability to update their authenticator two factor settings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We protect two factor updates behind user verification (re-authentication) to protect against attacks of opportunity
|
||||
/// (e.g. a user leaves their web vault unlocked). Most two factor options only require user verification (UV) when
|
||||
/// enabling or disabling the option, retrieving the current status usually isn't a sensitive operation. However,
|
||||
/// the status of authenticator two factor is sensitive because it reveals the user's secret key, which means both
|
||||
/// operations must be protected by UV.
|
||||
///
|
||||
/// TOTP as a UV option is only allowed to be used once, so we return this tokenable when retrieving the current status
|
||||
/// (and secret key) of authenticator two factor to give the user a means of passing UV when updating (enabling/disabling).
|
||||
/// </remarks>
|
||||
public class TwoFactorAuthenticatorUserVerificationTokenable : ExpiringTokenable
|
||||
{
|
||||
private static readonly TimeSpan _tokenLifetime = TimeSpan.FromMinutes(30);
|
||||
|
||||
public const string ClearTextPrefix = "TwoFactorAuthenticatorUserVerification";
|
||||
public const string DataProtectorPurpose = "TwoFactorAuthenticatorUserVerificationTokenDataProtector";
|
||||
public const string TokenIdentifier = "TwoFactorAuthenticatorUserVerificationToken";
|
||||
public string Identifier { get; set; } = TokenIdentifier;
|
||||
public Guid UserId { get; set; }
|
||||
public string Key { get; set; }
|
||||
|
||||
public override bool Valid => Identifier == TokenIdentifier &&
|
||||
UserId != default;
|
||||
|
||||
[JsonConstructor]
|
||||
public TwoFactorAuthenticatorUserVerificationTokenable()
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(_tokenLifetime);
|
||||
}
|
||||
|
||||
public TwoFactorAuthenticatorUserVerificationTokenable(User user, string key) : this()
|
||||
{
|
||||
UserId = user?.Id ?? default;
|
||||
Key = key;
|
||||
}
|
||||
|
||||
public bool TokenIsValid(User user, string key)
|
||||
{
|
||||
if (UserId == default
|
||||
|| user == null
|
||||
|| string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return UserId == user.Id && Key == key;
|
||||
}
|
||||
|
||||
protected override bool TokenIsValid() =>
|
||||
Identifier == TokenIdentifier
|
||||
&& UserId != default
|
||||
&& !string.IsNullOrWhiteSpace(Key);
|
||||
}
|
@ -131,6 +131,7 @@ public static class FeatureFlagKeys
|
||||
public const string AC2828_ProviderPortalMembersPage = "AC-2828_provider-portal-members-page";
|
||||
public const string ProviderClientVaultPrivacyBanner = "ac-2833-provider-client-vault-privacy-banner";
|
||||
public const string DeviceTrustLogging = "pm-8285-device-trust-logging";
|
||||
public const string AuthenticatorTwoFactorToken = "authenticator-2fa-token";
|
||||
|
||||
public static List<string> GetAllKeys()
|
||||
{
|
||||
|
Reference in New Issue
Block a user