mirror of
https://github.com/bitwarden/server
synced 2026-01-30 00:03:48 +00:00
Initial refactor
This commit is contained in:
@@ -26,7 +26,7 @@ public class UserDecryptionOptions : ResponseModel
|
||||
/// Gets or sets the WebAuthn PRF decryption keys.
|
||||
/// </summary>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public WebAuthnPrfDecryptionOption? WebAuthnPrfOption { get; set; }
|
||||
public WebAuthnPrfDecryptionOption[]? WebAuthnPrfOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets information regarding this users trusted device decryption setup.
|
||||
@@ -45,13 +45,19 @@ public class WebAuthnPrfDecryptionOption
|
||||
{
|
||||
public string EncryptedPrivateKey { get; }
|
||||
public string EncryptedUserKey { get; }
|
||||
public string CredentialId { get; }
|
||||
public string[] Transports { get; }
|
||||
|
||||
public WebAuthnPrfDecryptionOption(
|
||||
string encryptedPrivateKey,
|
||||
string encryptedUserKey)
|
||||
string encryptedUserKey,
|
||||
string credentialId,
|
||||
string[]? transports = null)
|
||||
{
|
||||
EncryptedPrivateKey = encryptedPrivateKey;
|
||||
EncryptedUserKey = encryptedUserKey;
|
||||
CredentialId = credentialId;
|
||||
Transports = transports ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,6 @@ public interface IUserDecryptionOptionsBuilder
|
||||
IUserDecryptionOptionsBuilder ForUser(User user);
|
||||
IUserDecryptionOptionsBuilder WithDevice(Device device);
|
||||
IUserDecryptionOptionsBuilder WithSso(SsoConfig ssoConfig);
|
||||
IUserDecryptionOptionsBuilder WithWebAuthnLoginCredential(WebAuthnCredential credential);
|
||||
IUserDecryptionOptionsBuilder WithWebAuthnLoginCredentials(IEnumerable<WebAuthnCredential> credentials);
|
||||
Task<UserDecryptionOptions> BuildAsync();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public class WebAuthnGrantValidator : BaseRequestValidator<ExtensionGrantValidat
|
||||
private readonly IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> _assertionOptionsDataProtector;
|
||||
private readonly IAssertWebAuthnLoginCredentialCommand _assertWebAuthnLoginCredentialCommand;
|
||||
private readonly IDeviceValidator _deviceValidator;
|
||||
private readonly IWebAuthnCredentialRepository _webAuthnCredentialRepository;
|
||||
|
||||
public WebAuthnGrantValidator(
|
||||
UserManager<User> userManager,
|
||||
@@ -50,7 +51,8 @@ public class WebAuthnGrantValidator : BaseRequestValidator<ExtensionGrantValidat
|
||||
IAssertWebAuthnLoginCredentialCommand assertWebAuthnLoginCredentialCommand,
|
||||
IPolicyRequirementQuery policyRequirementQuery,
|
||||
IAuthRequestRepository authRequestRepository,
|
||||
IMailService mailService)
|
||||
IMailService mailService,
|
||||
IWebAuthnCredentialRepository webAuthnCredentialRepository)
|
||||
: base(
|
||||
userManager,
|
||||
userService,
|
||||
@@ -73,6 +75,7 @@ public class WebAuthnGrantValidator : BaseRequestValidator<ExtensionGrantValidat
|
||||
_assertionOptionsDataProtector = assertionOptionsDataProtector;
|
||||
_assertWebAuthnLoginCredentialCommand = assertWebAuthnLoginCredentialCommand;
|
||||
_deviceValidator = deviceValidator;
|
||||
_webAuthnCredentialRepository = webAuthnCredentialRepository;
|
||||
}
|
||||
|
||||
string IExtensionGrantValidator.GrantType => "webauthn";
|
||||
@@ -98,7 +101,8 @@ public class WebAuthnGrantValidator : BaseRequestValidator<ExtensionGrantValidat
|
||||
}
|
||||
|
||||
var (user, credential) = await _assertWebAuthnLoginCredentialCommand.AssertWebAuthnLoginCredential(token.Options, deviceResponse);
|
||||
UserDecryptionOptionsBuilder.WithWebAuthnLoginCredential(credential);
|
||||
var allCredentials = await _webAuthnCredentialRepository.GetManyByUserIdAsync(user.Id);
|
||||
UserDecryptionOptionsBuilder.WithWebAuthnLoginCredentials(allCredentials);
|
||||
|
||||
await ValidateAsync(context, context.Request, new CustomValidatorRequestContext { User = user });
|
||||
}
|
||||
|
||||
@@ -57,15 +57,25 @@ public class UserDecryptionOptionsBuilder : IUserDecryptionOptionsBuilder
|
||||
|
||||
public IUserDecryptionOptionsBuilder WithDevice(Device device)
|
||||
{
|
||||
_device = device;
|
||||
_device = device;>
|
||||
return this;
|
||||
}
|
||||
|
||||
public IUserDecryptionOptionsBuilder WithWebAuthnLoginCredential(WebAuthnCredential credential)
|
||||
public IUserDecryptionOptionsBuilder WithWebAuthnLoginCredentials(IEnumerable<WebAuthnCredential> credentials)
|
||||
{
|
||||
if (credential.GetPrfStatus() == WebAuthnPrfStatus.Enabled)
|
||||
var prfEnabledCredentials = credentials
|
||||
.Where(c => c.GetPrfStatus() == WebAuthnPrfStatus.Enabled)
|
||||
.Select(c => new WebAuthnPrfDecryptionOption(
|
||||
c.EncryptedPrivateKey,
|
||||
c.EncryptedUserKey,
|
||||
c.CredentialId,
|
||||
[] // Stored credentials currently lack Transports, just send an empty array for now
|
||||
))
|
||||
.ToArray();
|
||||
|
||||
if (prfEnabledCredentials.Length > 0)
|
||||
{
|
||||
_options.WebAuthnPrfOption = new WebAuthnPrfDecryptionOption(credential.EncryptedPrivateKey, credential.EncryptedUserKey);
|
||||
_options.WebAuthnPrfOptions = prfEnabledCredentials;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -585,7 +585,7 @@ public class BaseRequestValidatorTests
|
||||
_userDecryptionOptionsBuilder.ForUser(Arg.Any<User>()).Returns(_userDecryptionOptionsBuilder);
|
||||
_userDecryptionOptionsBuilder.WithDevice(Arg.Any<Device>()).Returns(_userDecryptionOptionsBuilder);
|
||||
_userDecryptionOptionsBuilder.WithSso(Arg.Any<SsoConfig>()).Returns(_userDecryptionOptionsBuilder);
|
||||
_userDecryptionOptionsBuilder.WithWebAuthnLoginCredential(Arg.Any<WebAuthnCredential>()).Returns(_userDecryptionOptionsBuilder);
|
||||
_userDecryptionOptionsBuilder.WithWebAuthnLoginCredentials(Arg.Any<IEnumerable<WebAuthnCredential>>()).Returns(_userDecryptionOptionsBuilder);
|
||||
_userDecryptionOptionsBuilder.BuildAsync().Returns(Task.FromResult(new UserDecryptionOptions
|
||||
{
|
||||
HasMasterPassword = false,
|
||||
@@ -626,7 +626,7 @@ public class BaseRequestValidatorTests
|
||||
_userDecryptionOptionsBuilder.ForUser(Arg.Any<User>()).Returns(_userDecryptionOptionsBuilder);
|
||||
_userDecryptionOptionsBuilder.WithDevice(Arg.Any<Device>()).Returns(_userDecryptionOptionsBuilder);
|
||||
_userDecryptionOptionsBuilder.WithSso(Arg.Any<SsoConfig>()).Returns(_userDecryptionOptionsBuilder);
|
||||
_userDecryptionOptionsBuilder.WithWebAuthnLoginCredential(Arg.Any<WebAuthnCredential>()).Returns(_userDecryptionOptionsBuilder);
|
||||
_userDecryptionOptionsBuilder.WithWebAuthnLoginCredentials(Arg.Any<IEnumerable<WebAuthnCredential>>()).Returns(_userDecryptionOptionsBuilder);
|
||||
_userDecryptionOptionsBuilder.BuildAsync().Returns(Task.FromResult(new UserDecryptionOptions
|
||||
{
|
||||
HasMasterPassword = true,
|
||||
|
||||
@@ -41,7 +41,7 @@ public class UserDecryptionOptionsBuilderTests
|
||||
[BitAutoData(true, false, true)] // EncryptedPrivateKey and EncryptedUserKey are non-null, EncryptedPublicKey is null
|
||||
[BitAutoData(true, true, false)] // EncryptedPrivateKey and EncryptedPublicKey are non-null, EncryptedUserKey is null
|
||||
[BitAutoData(false, true, true)] // EncryptedPublicKey and EncryptedUserKey are non-null, EncryptedPrivateKey is null
|
||||
public async Task WithWebAuthnLoginCredential_VariousKeyCombinations_ShouldReturnCorrectPrfOption(
|
||||
public async Task WithWebAuthnLoginCredentials_VariousKeyCombinations_ShouldReturnCorrectPrfOption(
|
||||
bool hasEncryptedPrivateKey,
|
||||
bool hasEncryptedPublicKey,
|
||||
bool hasEncryptedUserKey,
|
||||
@@ -51,17 +51,19 @@ public class UserDecryptionOptionsBuilderTests
|
||||
credential.EncryptedPublicKey = hasEncryptedPublicKey ? "encryptedPublicKey" : null;
|
||||
credential.EncryptedUserKey = hasEncryptedUserKey ? "encryptedUserKey" : null;
|
||||
|
||||
var result = await _builder.WithWebAuthnLoginCredential(credential).BuildAsync();
|
||||
var result = await _builder.WithWebAuthnLoginCredentials([credential]).BuildAsync();
|
||||
|
||||
if (credential.GetPrfStatus() == WebAuthnPrfStatus.Enabled)
|
||||
{
|
||||
Assert.NotNull(result.WebAuthnPrfOption);
|
||||
Assert.Equal(credential.EncryptedPrivateKey, result.WebAuthnPrfOption!.EncryptedPrivateKey);
|
||||
Assert.Equal(credential.EncryptedUserKey, result.WebAuthnPrfOption!.EncryptedUserKey);
|
||||
Assert.NotNull(result.WebAuthnPrfOptions);
|
||||
Assert.Single(result.WebAuthnPrfOptions);
|
||||
Assert.Equal(credential.EncryptedPrivateKey, result.WebAuthnPrfOptions![0].EncryptedPrivateKey);
|
||||
Assert.Equal(credential.CredentialId, result.WebAuthnPrfOptions![0].CredentialId);
|
||||
Assert.Equal(credential.EncryptedUserKey, result.WebAuthnPrfOptions![0].EncryptedUserKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Null(result.WebAuthnPrfOption);
|
||||
Assert.Null(result.WebAuthnPrfOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user