mirror of
https://github.com/bitwarden/server
synced 2025-12-17 00:33:23 +00:00
* 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>
334 lines
11 KiB
C#
334 lines
11 KiB
C#
using System.ComponentModel.DataAnnotations;
|
|
using Bit.Api.Auth.Models.Request.Accounts;
|
|
using Bit.Core.AdminConsole.Entities;
|
|
using Bit.Core.Auth.Enums;
|
|
using Bit.Core.Auth.Models;
|
|
using Bit.Core.Auth.Utilities;
|
|
using Bit.Core.Entities;
|
|
using Fido2NetLib;
|
|
|
|
namespace Bit.Api.Auth.Models.Request;
|
|
|
|
public class UpdateTwoFactorAuthenticatorRequestModel : SecretVerificationRequestModel
|
|
{
|
|
[Required]
|
|
[StringLength(50)]
|
|
public string Token { get; set; }
|
|
[Required]
|
|
[StringLength(50)]
|
|
public string Key { get; set; }
|
|
public string UserVerificationToken { get; set; }
|
|
public User ToUser(User existingUser)
|
|
{
|
|
var providers = existingUser.GetTwoFactorProviders();
|
|
if (providers == null)
|
|
{
|
|
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
|
}
|
|
else if (providers.ContainsKey(TwoFactorProviderType.Authenticator))
|
|
{
|
|
providers.Remove(TwoFactorProviderType.Authenticator);
|
|
}
|
|
|
|
providers.Add(TwoFactorProviderType.Authenticator, new TwoFactorProvider
|
|
{
|
|
MetaData = new Dictionary<string, object> { ["Key"] = Key },
|
|
Enabled = true
|
|
});
|
|
existingUser.SetTwoFactorProviders(providers);
|
|
return existingUser;
|
|
}
|
|
}
|
|
|
|
public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IValidatableObject
|
|
{
|
|
/*
|
|
To support both v2 and v4 we need to remove the required annotation from the properties.
|
|
todo - the required annotation will be added back in PM-8107.
|
|
*/
|
|
[StringLength(50)]
|
|
public string ClientId { get; set; }
|
|
[StringLength(50)]
|
|
public string ClientSecret { get; set; }
|
|
//todo - will remove SKey and IKey with PM-8107
|
|
[StringLength(50)]
|
|
public string IntegrationKey { get; set; }
|
|
//todo - will remove SKey and IKey with PM-8107
|
|
[StringLength(50)]
|
|
public string SecretKey { get; set; }
|
|
[Required]
|
|
[StringLength(50)]
|
|
public string Host { get; set; }
|
|
|
|
public User ToUser(User existingUser)
|
|
{
|
|
var providers = existingUser.GetTwoFactorProviders();
|
|
if (providers == null)
|
|
{
|
|
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
|
}
|
|
else if (providers.ContainsKey(TwoFactorProviderType.Duo))
|
|
{
|
|
providers.Remove(TwoFactorProviderType.Duo);
|
|
}
|
|
|
|
Temporary_SyncDuoParams();
|
|
|
|
providers.Add(TwoFactorProviderType.Duo, new TwoFactorProvider
|
|
{
|
|
MetaData = new Dictionary<string, object>
|
|
{
|
|
//todo - will remove SKey and IKey with PM-8107
|
|
["SKey"] = SecretKey,
|
|
["IKey"] = IntegrationKey,
|
|
["ClientSecret"] = ClientSecret,
|
|
["ClientId"] = ClientId,
|
|
["Host"] = Host
|
|
},
|
|
Enabled = true
|
|
});
|
|
existingUser.SetTwoFactorProviders(providers);
|
|
return existingUser;
|
|
}
|
|
|
|
public Organization ToOrganization(Organization existingOrg)
|
|
{
|
|
var providers = existingOrg.GetTwoFactorProviders();
|
|
if (providers == null)
|
|
{
|
|
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
|
}
|
|
else if (providers.ContainsKey(TwoFactorProviderType.OrganizationDuo))
|
|
{
|
|
providers.Remove(TwoFactorProviderType.OrganizationDuo);
|
|
}
|
|
|
|
Temporary_SyncDuoParams();
|
|
|
|
providers.Add(TwoFactorProviderType.OrganizationDuo, new TwoFactorProvider
|
|
{
|
|
MetaData = new Dictionary<string, object>
|
|
{
|
|
//todo - will remove SKey and IKey with PM-8107
|
|
["SKey"] = SecretKey,
|
|
["IKey"] = IntegrationKey,
|
|
["ClientSecret"] = ClientSecret,
|
|
["ClientId"] = ClientId,
|
|
["Host"] = Host
|
|
},
|
|
Enabled = true
|
|
});
|
|
existingOrg.SetTwoFactorProviders(providers);
|
|
return existingOrg;
|
|
}
|
|
|
|
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
|
{
|
|
if (!DuoApi.ValidHost(Host))
|
|
{
|
|
yield return new ValidationResult("Host is invalid.", [nameof(Host)]);
|
|
}
|
|
if (string.IsNullOrWhiteSpace(ClientSecret) && string.IsNullOrWhiteSpace(ClientId) &&
|
|
string.IsNullOrWhiteSpace(SecretKey) && string.IsNullOrWhiteSpace(IntegrationKey))
|
|
{
|
|
yield return new ValidationResult("Neither v2 or v4 values are valid.", [nameof(IntegrationKey), nameof(SecretKey), nameof(ClientSecret), nameof(ClientId)]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
use this method to ensure that both v2 params and v4 params are in sync
|
|
todo will be removed in pm-8107
|
|
*/
|
|
private void Temporary_SyncDuoParams()
|
|
{
|
|
// Even if IKey and SKey exist prioritize v4 params ClientId and ClientSecret
|
|
if (!string.IsNullOrWhiteSpace(ClientSecret) && !string.IsNullOrWhiteSpace(ClientId))
|
|
{
|
|
SecretKey = ClientSecret;
|
|
IntegrationKey = ClientId;
|
|
}
|
|
else if (!string.IsNullOrWhiteSpace(SecretKey) && !string.IsNullOrWhiteSpace(IntegrationKey))
|
|
{
|
|
ClientSecret = SecretKey;
|
|
ClientId = IntegrationKey;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class UpdateTwoFactorYubicoOtpRequestModel : SecretVerificationRequestModel, IValidatableObject
|
|
{
|
|
public string Key1 { get; set; }
|
|
public string Key2 { get; set; }
|
|
public string Key3 { get; set; }
|
|
public string Key4 { get; set; }
|
|
public string Key5 { get; set; }
|
|
[Required]
|
|
public bool? Nfc { get; set; }
|
|
|
|
public User ToUser(User existingUser)
|
|
{
|
|
var providers = existingUser.GetTwoFactorProviders();
|
|
if (providers == null)
|
|
{
|
|
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
|
}
|
|
else if (providers.ContainsKey(TwoFactorProviderType.YubiKey))
|
|
{
|
|
providers.Remove(TwoFactorProviderType.YubiKey);
|
|
}
|
|
|
|
providers.Add(TwoFactorProviderType.YubiKey, new TwoFactorProvider
|
|
{
|
|
MetaData = new Dictionary<string, object>
|
|
{
|
|
["Key1"] = FormatKey(Key1),
|
|
["Key2"] = FormatKey(Key2),
|
|
["Key3"] = FormatKey(Key3),
|
|
["Key4"] = FormatKey(Key4),
|
|
["Key5"] = FormatKey(Key5),
|
|
["Nfc"] = Nfc.Value
|
|
},
|
|
Enabled = true
|
|
});
|
|
existingUser.SetTwoFactorProviders(providers);
|
|
return existingUser;
|
|
}
|
|
|
|
private string FormatKey(string keyValue)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(keyValue))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return keyValue.Substring(0, 12);
|
|
}
|
|
|
|
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(Key1) && string.IsNullOrWhiteSpace(Key2) && string.IsNullOrWhiteSpace(Key3) &&
|
|
string.IsNullOrWhiteSpace(Key4) && string.IsNullOrWhiteSpace(Key5))
|
|
{
|
|
yield return new ValidationResult("A key is required.", new string[] { nameof(Key1) });
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(Key1) && Key1.Length < 12)
|
|
{
|
|
yield return new ValidationResult("Key 1 in invalid.", new string[] { nameof(Key1) });
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(Key2) && Key2.Length < 12)
|
|
{
|
|
yield return new ValidationResult("Key 2 in invalid.", new string[] { nameof(Key2) });
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(Key3) && Key3.Length < 12)
|
|
{
|
|
yield return new ValidationResult("Key 3 in invalid.", new string[] { nameof(Key3) });
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(Key4) && Key4.Length < 12)
|
|
{
|
|
yield return new ValidationResult("Key 4 in invalid.", new string[] { nameof(Key4) });
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(Key5) && Key5.Length < 12)
|
|
{
|
|
yield return new ValidationResult("Key 5 in invalid.", new string[] { nameof(Key5) });
|
|
}
|
|
}
|
|
}
|
|
|
|
public class TwoFactorEmailRequestModel : SecretVerificationRequestModel
|
|
{
|
|
[Required]
|
|
[EmailAddress]
|
|
[StringLength(256)]
|
|
public string Email { get; set; }
|
|
public string AuthRequestId { get; set; }
|
|
// An auth session token used for obtaining email and as an authN factor for the sending of emailed 2FA OTPs.
|
|
public string SsoEmail2FaSessionToken { get; set; }
|
|
public User ToUser(User existingUser)
|
|
{
|
|
var providers = existingUser.GetTwoFactorProviders();
|
|
if (providers == null)
|
|
{
|
|
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
|
}
|
|
else if (providers.ContainsKey(TwoFactorProviderType.Email))
|
|
{
|
|
providers.Remove(TwoFactorProviderType.Email);
|
|
}
|
|
|
|
providers.Add(TwoFactorProviderType.Email, new TwoFactorProvider
|
|
{
|
|
MetaData = new Dictionary<string, object> { ["Email"] = Email.ToLowerInvariant() },
|
|
Enabled = true
|
|
});
|
|
existingUser.SetTwoFactorProviders(providers);
|
|
return existingUser;
|
|
}
|
|
|
|
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
|
{
|
|
if (string.IsNullOrEmpty(Secret) && string.IsNullOrEmpty(AuthRequestAccessCode) && string.IsNullOrEmpty((SsoEmail2FaSessionToken)))
|
|
{
|
|
yield return new ValidationResult("MasterPasswordHash, OTP, AccessCode, or SsoEmail2faSessionToken must be supplied.");
|
|
}
|
|
}
|
|
}
|
|
|
|
public class TwoFactorWebAuthnRequestModel : TwoFactorWebAuthnDeleteRequestModel
|
|
{
|
|
[Required]
|
|
public AuthenticatorAttestationRawResponse DeviceResponse { get; set; }
|
|
public string Name { get; set; }
|
|
}
|
|
|
|
public class TwoFactorWebAuthnDeleteRequestModel : SecretVerificationRequestModel, IValidatableObject
|
|
{
|
|
[Required]
|
|
public int? Id { get; set; }
|
|
|
|
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
|
{
|
|
foreach (var validationResult in base.Validate(validationContext))
|
|
{
|
|
yield return validationResult;
|
|
}
|
|
|
|
if (!Id.HasValue || Id < 0 || Id > 5)
|
|
{
|
|
yield return new ValidationResult("Invalid Key Id", new string[] { nameof(Id) });
|
|
}
|
|
}
|
|
}
|
|
|
|
public class UpdateTwoFactorEmailRequestModel : TwoFactorEmailRequestModel
|
|
{
|
|
[Required]
|
|
[StringLength(50)]
|
|
public string Token { get; set; }
|
|
}
|
|
|
|
public class TwoFactorProviderRequestModel : SecretVerificationRequestModel
|
|
{
|
|
[Required]
|
|
public TwoFactorProviderType? Type { get; set; }
|
|
}
|
|
|
|
public class TwoFactorRecoveryRequestModel : TwoFactorEmailRequestModel
|
|
{
|
|
[Required]
|
|
[StringLength(32)]
|
|
public string RecoveryCode { get; set; }
|
|
}
|
|
|
|
public class TwoFactorAuthenticatorDisableRequestModel : TwoFactorProviderRequestModel
|
|
{
|
|
[Required]
|
|
public string UserVerificationToken { get; set; }
|
|
[Required]
|
|
public string Key { get; set; }
|
|
}
|