mirror of
https://github.com/bitwarden/server.git
synced 2025-02-16 01:51:21 +01:00
Remove U2F APIs again (#1319)
* Revert "U2F (#1304)"
This reverts commit ce4f025a0c
.
* Avoid removing WebAuthn fixes
This commit is contained in:
parent
d21ca83a20
commit
a47b86a995
@ -220,48 +220,6 @@ namespace Bit.Api.Controllers
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-u2f")]
|
||||
public async Task<TwoFactorU2fResponseModel> GetU2f([FromBody]TwoFactorRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model.MasterPasswordHash, true);
|
||||
var response = new TwoFactorU2fResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-u2f-challenge")]
|
||||
public async Task<TwoFactorU2fResponseModel.ChallengeModel> GetU2fChallenge(
|
||||
[FromBody]TwoFactorRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model.MasterPasswordHash, true);
|
||||
var reg = await _userService.StartU2fRegistrationAsync(user);
|
||||
var challenge = new TwoFactorU2fResponseModel.ChallengeModel(user, reg);
|
||||
return challenge;
|
||||
}
|
||||
|
||||
[HttpPut("u2f")]
|
||||
[HttpPost("u2f")]
|
||||
public async Task<TwoFactorU2fResponseModel> PutU2f([FromBody]TwoFactorU2fRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model.MasterPasswordHash, true);
|
||||
var success = await _userService.CompleteU2fRegistrationAsync(
|
||||
user, model.Id.Value, model.Name, model.DeviceResponse);
|
||||
if (!success)
|
||||
{
|
||||
throw new BadRequestException("Unable to complete U2F key registration.");
|
||||
}
|
||||
var response = new TwoFactorU2fResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpDelete("u2f")]
|
||||
public async Task<TwoFactorU2fResponseModel> DeleteU2f([FromBody]TwoFactorU2fDeleteRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model.MasterPasswordHash, true);
|
||||
await _userService.DeleteU2fKeyAsync(user, model.Id.Value);
|
||||
var response = new TwoFactorU2fResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-webauthn")]
|
||||
public async Task<TwoFactorWebAuthnResponseModel> GetWebAuthn([FromBody]TwoFactorRequestModel model)
|
||||
{
|
||||
|
@ -224,27 +224,6 @@ namespace Bit.Core.Models.Api
|
||||
}
|
||||
}
|
||||
|
||||
public class TwoFactorU2fRequestModel : TwoFactorU2fDeleteRequestModel
|
||||
{
|
||||
[Required]
|
||||
public string DeviceResponse { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public class TwoFactorU2fDeleteRequestModel : TwoFactorRequestModel, IValidatableObject
|
||||
{
|
||||
[Required]
|
||||
public int? Id { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (!Id.HasValue || Id < 0 || Id > 5)
|
||||
{
|
||||
yield return new ValidationResult("Invalid Key Id", new string[] { nameof(Id) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TwoFactorWebAuthnRequestModel : TwoFactorWebAuthnDeleteRequestModel
|
||||
{
|
||||
[Required]
|
||||
|
@ -1,59 +0,0 @@
|
||||
using System;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Enums;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class TwoFactorU2fResponseModel : ResponseModel
|
||||
{
|
||||
public TwoFactorU2fResponseModel(User user)
|
||||
: base("twoFactorU2f")
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
Enabled = provider?.Enabled ?? false;
|
||||
Keys = provider?.MetaData?.Select(k => new KeyModel(k.Key,
|
||||
new TwoFactorProvider.U2fMetaData((dynamic)k.Value)));
|
||||
}
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public IEnumerable<KeyModel> Keys { get; set; }
|
||||
|
||||
public class KeyModel
|
||||
{
|
||||
public KeyModel(string id, TwoFactorProvider.U2fMetaData data)
|
||||
{
|
||||
Name = data.Name;
|
||||
Id = Convert.ToInt32(id.Replace("Key", string.Empty));
|
||||
Compromised = data.Compromised;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public int Id { get; set; }
|
||||
public bool Compromised { get; set; }
|
||||
}
|
||||
|
||||
public class ChallengeModel
|
||||
{
|
||||
public ChallengeModel(User user, U2fRegistration registration)
|
||||
{
|
||||
UserId = user.Id.ToString();
|
||||
AppId = registration.AppId;
|
||||
Challenge = registration.Challenge;
|
||||
Version = registration.Version;
|
||||
}
|
||||
|
||||
public string UserId { get; set; }
|
||||
public string AppId { get; set; }
|
||||
public string Challenge { get; set; }
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public class U2fRegistration
|
||||
{
|
||||
public string AppId { get; set; }
|
||||
public string Challenge { get; set; }
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
@ -24,9 +24,6 @@ namespace Bit.Core.Services
|
||||
Task SendMasterPasswordHintAsync(string email);
|
||||
Task SendTwoFactorEmailAsync(User user);
|
||||
Task<bool> VerifyTwoFactorEmailAsync(User user, string token);
|
||||
Task<U2fRegistration> StartU2fRegistrationAsync(User user);
|
||||
Task<bool> DeleteU2fKeyAsync(User user, int id);
|
||||
Task<bool> CompleteU2fRegistrationAsync(User user, int id, string name, string deviceResponse);
|
||||
Task<CredentialCreateOptions> StartWebAuthnRegistrationAsync(User user);
|
||||
Task<bool> DeleteWebAuthnKeyAsync(User user, int id);
|
||||
Task<bool> CompleteWebAuthRegistrationAsync(User user, int value, string name, AuthenticatorAttestationRawResponse attestationResponse);
|
||||
|
@ -18,7 +18,6 @@ using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Settings;
|
||||
using U2F.Core.Exceptions;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
@ -36,7 +35,6 @@ namespace Bit.Core.Services
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IU2fRepository _u2fRepository;
|
||||
private readonly IMailService _mailService;
|
||||
private readonly IPushNotificationService _pushService;
|
||||
private readonly IdentityErrorDescriber _identityErrorDescriber;
|
||||
@ -60,7 +58,6 @@ namespace Bit.Core.Services
|
||||
ICipherRepository cipherRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IU2fRepository u2fRepository,
|
||||
IMailService mailService,
|
||||
IPushNotificationService pushService,
|
||||
IUserStore<User> store,
|
||||
@ -98,7 +95,6 @@ namespace Bit.Core.Services
|
||||
_cipherRepository = cipherRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_organizationRepository = organizationRepository;
|
||||
_u2fRepository = u2fRepository;
|
||||
_mailService = mailService;
|
||||
_pushService = pushService;
|
||||
_identityOptions = optionsAccessor?.Value ?? new IdentityOptions();
|
||||
@ -367,133 +363,6 @@ namespace Bit.Core.Services
|
||||
"2faEmail:" + email, token);
|
||||
}
|
||||
|
||||
public async Task<U2fRegistration> StartU2fRegistrationAsync(User user)
|
||||
{
|
||||
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
||||
var reg = U2fLib.StartRegistration(CoreHelpers.U2fAppIdUrl(_globalSettings));
|
||||
await _u2fRepository.CreateAsync(new U2f
|
||||
{
|
||||
AppId = reg.AppId,
|
||||
Challenge = reg.Challenge,
|
||||
Version = reg.Version,
|
||||
UserId = user.Id,
|
||||
CreationDate = DateTime.UtcNow
|
||||
});
|
||||
|
||||
return new U2fRegistration
|
||||
{
|
||||
AppId = reg.AppId,
|
||||
Challenge = reg.Challenge,
|
||||
Version = reg.Version
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<bool> CompleteU2fRegistrationAsync(User user, int id, string name, string deviceResponse)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(deviceResponse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var challenges = await _u2fRepository.GetManyByUserIdAsync(user.Id);
|
||||
if (!challenges?.Any() ?? true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var registerResponse = BaseModel.FromJson<RegisterResponse>(deviceResponse);
|
||||
|
||||
try
|
||||
{
|
||||
var challenge = challenges.OrderBy(i => i.Id).Last(i => i.KeyHandle == null);
|
||||
var startedReg = new StartedRegistration(challenge.Challenge, challenge.AppId);
|
||||
var reg = U2fLib.FinishRegistration(startedReg, registerResponse);
|
||||
|
||||
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
||||
|
||||
// Add device
|
||||
var providers = user.GetTwoFactorProviders();
|
||||
if (providers == null)
|
||||
{
|
||||
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||
}
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
if (provider == null)
|
||||
{
|
||||
provider = new TwoFactorProvider();
|
||||
}
|
||||
if (provider.MetaData == null)
|
||||
{
|
||||
provider.MetaData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
if (provider.MetaData.Count >= 5)
|
||||
{
|
||||
// Can only register up to 5 keys
|
||||
return false;
|
||||
}
|
||||
|
||||
var keyId = $"Key{id}";
|
||||
if (provider.MetaData.ContainsKey(keyId))
|
||||
{
|
||||
provider.MetaData.Remove(keyId);
|
||||
}
|
||||
|
||||
provider.Enabled = true;
|
||||
provider.MetaData.Add(keyId, new TwoFactorProvider.U2fMetaData
|
||||
{
|
||||
Name = name,
|
||||
KeyHandle = reg.KeyHandle == null ? null : Utils.ByteArrayToBase64String(reg.KeyHandle),
|
||||
PublicKey = reg.PublicKey == null ? null : Utils.ByteArrayToBase64String(reg.PublicKey),
|
||||
Certificate = reg.AttestationCert == null ? null : Utils.ByteArrayToBase64String(reg.AttestationCert),
|
||||
Compromised = false,
|
||||
Counter = reg.Counter
|
||||
});
|
||||
|
||||
if (providers.ContainsKey(TwoFactorProviderType.U2f))
|
||||
{
|
||||
providers.Remove(TwoFactorProviderType.U2f);
|
||||
}
|
||||
|
||||
providers.Add(TwoFactorProviderType.U2f, provider);
|
||||
user.SetTwoFactorProviders(providers);
|
||||
await UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.U2f);
|
||||
return true;
|
||||
}
|
||||
catch (U2fException e)
|
||||
{
|
||||
Logger.LogError(e, "Complete U2F registration error.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteU2fKeyAsync(User user, int id)
|
||||
{
|
||||
var providers = user.GetTwoFactorProviders();
|
||||
if (providers == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var keyName = $"Key{id}";
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
if (!provider?.MetaData?.ContainsKey(keyName) ?? true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (provider.MetaData.Count < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
provider.MetaData.Remove(keyName);
|
||||
providers[TwoFactorProviderType.U2f] = provider;
|
||||
user.SetTwoFactorProviders(providers);
|
||||
await UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.U2f);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<CredentialCreateOptions> StartWebAuthnRegistrationAsync(User user)
|
||||
{
|
||||
var providers = user.GetTwoFactorProviders();
|
||||
|
@ -23,7 +23,6 @@ namespace Bit.Core.Test.Services
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IU2fRepository _u2fRepository;
|
||||
private readonly IMailService _mailService;
|
||||
private readonly IPushNotificationService _pushService;
|
||||
private readonly IUserStore<User> _userStore;
|
||||
@ -53,7 +52,6 @@ namespace Bit.Core.Test.Services
|
||||
_cipherRepository = Substitute.For<ICipherRepository>();
|
||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
||||
_u2fRepository = Substitute.For<IU2fRepository>();
|
||||
_mailService = Substitute.For<IMailService>();
|
||||
_pushService = Substitute.For<IPushNotificationService>();
|
||||
_userStore = Substitute.For<IUserStore<User>>();
|
||||
@ -82,7 +80,6 @@ namespace Bit.Core.Test.Services
|
||||
_cipherRepository,
|
||||
_organizationUserRepository,
|
||||
_organizationRepository,
|
||||
_u2fRepository,
|
||||
_mailService,
|
||||
_pushService,
|
||||
_userStore,
|
||||
|
Loading…
Reference in New Issue
Block a user