mirror of
https://github.com/bitwarden/server.git
synced 2025-05-20 19:14:32 -05:00
add support for multiple u2f keys
This commit is contained in:
parent
7176e0ea22
commit
bfc74060b6
@ -199,18 +199,18 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<TwoFactorU2fResponseModel> GetU2f([FromBody]TwoFactorRequestModel model)
|
public async Task<TwoFactorU2fResponseModel> GetU2f([FromBody]TwoFactorRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await CheckAsync(model.MasterPasswordHash, true);
|
var user = await CheckAsync(model.MasterPasswordHash, true);
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
var response = new TwoFactorU2fResponseModel(user);
|
||||||
if(provider == null || !provider.Enabled || (provider.MetaData?.Count ?? 0) > 0)
|
return response;
|
||||||
{
|
}
|
||||||
var reg = await _userService.StartU2fRegistrationAsync(user);
|
|
||||||
var response = new TwoFactorU2fResponseModel(user, provider, reg);
|
[HttpPost("get-u2f-challenge")]
|
||||||
return response;
|
public async Task<TwoFactorU2fResponseModel.ChallengeModel> GetU2fChallenge(
|
||||||
}
|
[FromBody]TwoFactorRequestModel model)
|
||||||
else
|
{
|
||||||
{
|
var user = await CheckAsync(model.MasterPasswordHash, true);
|
||||||
var response = new TwoFactorU2fResponseModel(user, provider);
|
var reg = await _userService.StartU2fRegistrationAsync(user);
|
||||||
return response;
|
var challenge = new TwoFactorU2fResponseModel.ChallengeModel(user, reg);
|
||||||
}
|
return challenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("u2f")]
|
[HttpPut("u2f")]
|
||||||
@ -218,7 +218,16 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<TwoFactorU2fResponseModel> PutU2f([FromBody]TwoFactorU2fRequestModel model)
|
public async Task<TwoFactorU2fResponseModel> PutU2f([FromBody]TwoFactorU2fRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await CheckAsync(model.MasterPasswordHash, true);
|
var user = await CheckAsync(model.MasterPasswordHash, true);
|
||||||
await _userService.CompleteU2fRegistrationAsync(user, model.DeviceResponse);
|
await _userService.CompleteU2fRegistrationAsync(user, model.Id.Value, model.Name, model.DeviceResponse);
|
||||||
|
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);
|
var response = new TwoFactorU2fResponseModel(user);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@ -284,7 +293,7 @@ namespace Bit.Api.Controllers
|
|||||||
var response = new TwoFactorProviderResponseModel(model.Type.Value, user);
|
var response = new TwoFactorProviderResponseModel(model.Type.Value, user);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("~/organizations/{id}/two-factor/disable")]
|
[HttpPut("~/organizations/{id}/two-factor/disable")]
|
||||||
[HttpPost("~/organizations/{id}/two-factor/disable")]
|
[HttpPost("~/organizations/{id}/two-factor/disable")]
|
||||||
public async Task<TwoFactorProviderResponseModel> PutOrganizationDisable(string id,
|
public async Task<TwoFactorProviderResponseModel> PutOrganizationDisable(string id,
|
||||||
|
@ -35,10 +35,9 @@
|
|||||||
<PackageReference Include="WindowsAzure.Storage" Version="9.3.2" />
|
<PackageReference Include="WindowsAzure.Storage" Version="9.3.2" />
|
||||||
<PackageReference Include="AspNetCoreRateLimit" Version="2.1.0" />
|
<PackageReference Include="AspNetCoreRateLimit" Version="2.1.0" />
|
||||||
<PackageReference Include="Braintree" Version="4.5.0" />
|
<PackageReference Include="Braintree" Version="4.5.0" />
|
||||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.2" />
|
|
||||||
<PackageReference Include="Sendgrid" Version="9.10.0" />
|
<PackageReference Include="Sendgrid" Version="9.10.0" />
|
||||||
<PackageReference Include="Stripe.net" Version="19.9.2" />
|
<PackageReference Include="Stripe.net" Version="19.9.2" />
|
||||||
<PackageReference Include="U2F.Core" Version="1.0.4" />
|
<PackageReference Include="U2F.Core" Version="2.0.0" />
|
||||||
<PackageReference Include="Otp.NET" Version="1.2.0" />
|
<PackageReference Include="Otp.NET" Version="1.2.0" />
|
||||||
<PackageReference Include="YubicoDotNetClient" Version="1.2.0" />
|
<PackageReference Include="YubicoDotNetClient" Version="1.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -10,6 +10,7 @@ using System.Linq;
|
|||||||
using U2fLib = U2F.Core.Crypto.U2F;
|
using U2fLib = U2F.Core.Crypto.U2F;
|
||||||
using U2F.Core.Models;
|
using U2F.Core.Models;
|
||||||
using U2F.Core.Exceptions;
|
using U2F.Core.Exceptions;
|
||||||
|
using U2F.Core.Utils;
|
||||||
using System;
|
using System;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@ -58,19 +59,7 @@ namespace Bit.Core.Identity
|
|||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||||
if(!HasProperMetaData(provider))
|
var keys = LoadKeys(provider);
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = new List<TwoFactorProvider.U2fMetaData>();
|
|
||||||
|
|
||||||
var key1 = new TwoFactorProvider.U2fMetaData((dynamic)provider.MetaData["Key1"]);
|
|
||||||
if(!key1?.Compromised ?? false)
|
|
||||||
{
|
|
||||||
keys.Add(key1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(keys.Count == 0)
|
if(keys.Count == 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@ -78,14 +67,16 @@ namespace Bit.Core.Identity
|
|||||||
|
|
||||||
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
||||||
|
|
||||||
|
var challengeBytes = U2fLib.Crypto.GenerateChallenge();
|
||||||
var challenges = new List<object>();
|
var challenges = new List<object>();
|
||||||
foreach(var key in keys)
|
foreach(var key in keys)
|
||||||
{
|
{
|
||||||
var registration = new DeviceRegistration(key.KeyHandleBytes, key.PublicKeyBytes,
|
var registration = new DeviceRegistration(key.Item2.KeyHandleBytes, key.Item2.PublicKeyBytes,
|
||||||
key.CertificateBytes, key.Counter);
|
key.Item2.CertificateBytes, key.Item2.Counter);
|
||||||
var auth = U2fLib.StartAuthentication(Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings), registration);
|
var auth = U2fLib.StartAuthentication(Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings), registration,
|
||||||
|
challengeBytes);
|
||||||
|
|
||||||
// Maybe move this to a bulk create when we support more than 1 key?
|
// TODO: Maybe move this to a bulk create?
|
||||||
await _u2fRepository.CreateAsync(new U2f
|
await _u2fRepository.CreateAsync(new U2f
|
||||||
{
|
{
|
||||||
AppId = auth.AppId,
|
AppId = auth.AppId,
|
||||||
@ -118,26 +109,14 @@ namespace Bit.Core.Identity
|
|||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||||
if(!HasProperMetaData(provider))
|
var keys = LoadKeys(provider);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = new List<TwoFactorProvider.U2fMetaData>();
|
|
||||||
|
|
||||||
var key1 = new TwoFactorProvider.U2fMetaData((dynamic)provider.MetaData["Key1"]);
|
|
||||||
if(!key1?.Compromised ?? false)
|
|
||||||
{
|
|
||||||
keys.Add(key1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(keys.Count == 0)
|
if(keys.Count == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var authenticateResponse = BaseModel.FromJson<AuthenticateResponse>(token);
|
var authenticateResponse = BaseModel.FromJson<AuthenticateResponse>(token);
|
||||||
var key = keys.FirstOrDefault(f => f.KeyHandle == authenticateResponse.KeyHandle);
|
var key = keys.FirstOrDefault(f => f.Item2.KeyHandle == authenticateResponse.KeyHandle);
|
||||||
|
|
||||||
if(key == null)
|
if(key == null)
|
||||||
{
|
{
|
||||||
@ -159,8 +138,8 @@ namespace Bit.Core.Identity
|
|||||||
}
|
}
|
||||||
|
|
||||||
var success = true;
|
var success = true;
|
||||||
var registration = new DeviceRegistration(key.KeyHandleBytes, key.PublicKeyBytes, key.CertificateBytes,
|
var registration = new DeviceRegistration(key.Item2.KeyHandleBytes, key.Item2.PublicKeyBytes,
|
||||||
key.Counter);
|
key.Item2.CertificateBytes, key.Item2.Counter);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var auth = new StartedAuthentication(challenge.Challenge, challenge.AppId, challenge.KeyHandle);
|
var auth = new StartedAuthentication(challenge.Challenge, challenge.AppId, challenge.KeyHandle);
|
||||||
@ -173,14 +152,14 @@ namespace Bit.Core.Identity
|
|||||||
|
|
||||||
// Update database
|
// Update database
|
||||||
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
||||||
key.Counter = registration.Counter;
|
key.Item2.Counter = registration.Counter;
|
||||||
if(key.Counter > 0)
|
if(key.Item2.Counter > 0)
|
||||||
{
|
{
|
||||||
key.Compromised = registration.IsCompromised;
|
key.Item2.Compromised = registration.IsCompromised;
|
||||||
}
|
}
|
||||||
|
|
||||||
var providers = user.GetTwoFactorProviders();
|
var providers = user.GetTwoFactorProviders();
|
||||||
providers[TwoFactorProviderType.U2f].MetaData["Key1"] = key;
|
providers[TwoFactorProviderType.U2f].MetaData[key.Item1] = key.Item2;
|
||||||
user.SetTwoFactorProviders(providers);
|
user.SetTwoFactorProviders(providers);
|
||||||
await manager.UpdateAsync(user);
|
await manager.UpdateAsync(user);
|
||||||
|
|
||||||
@ -189,7 +168,32 @@ namespace Bit.Core.Identity
|
|||||||
|
|
||||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||||
{
|
{
|
||||||
return provider?.MetaData != null && provider.MetaData.ContainsKey("Key1");
|
return (provider?.MetaData?.Count ?? 0) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Tuple<string, TwoFactorProvider.U2fMetaData>> LoadKeys(TwoFactorProvider provider)
|
||||||
|
{
|
||||||
|
var keys = new List<Tuple<string, TwoFactorProvider.U2fMetaData>>();
|
||||||
|
if(!HasProperMetaData(provider))
|
||||||
|
{
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support up to 5 keys
|
||||||
|
for(var i = 1; i <= 5; i++)
|
||||||
|
{
|
||||||
|
var keyName = $"Key{i}";
|
||||||
|
if(provider.MetaData.ContainsKey(keyName))
|
||||||
|
{
|
||||||
|
var key = new TwoFactorProvider.U2fMetaData((dynamic)provider.MetaData[keyName]);
|
||||||
|
if(!key?.Compromised ?? false)
|
||||||
|
{
|
||||||
|
keys.Add(new Tuple<string, TwoFactorProvider.U2fMetaData>(keyName, key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,10 +223,25 @@ namespace Bit.Core.Models.Api
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TwoFactorU2fRequestModel : TwoFactorRequestModel
|
public class TwoFactorU2fRequestModel : TwoFactorU2fDeleteRequestModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
public string DeviceResponse { get; set; }
|
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 UpdateTwoFactorEmailRequestModel : TwoFactorEmailRequestModel
|
public class UpdateTwoFactorEmailRequestModel : TwoFactorEmailRequestModel
|
||||||
|
@ -2,26 +2,13 @@
|
|||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Api
|
namespace Bit.Core.Models.Api
|
||||||
{
|
{
|
||||||
public class TwoFactorU2fResponseModel : ResponseModel
|
public class TwoFactorU2fResponseModel : ResponseModel
|
||||||
{
|
{
|
||||||
public TwoFactorU2fResponseModel(User user, TwoFactorProvider provider, U2fRegistration registration = null)
|
|
||||||
: base("twoFactorU2f")
|
|
||||||
{
|
|
||||||
if(user == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(user));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(registration != null)
|
|
||||||
{
|
|
||||||
Challenge = new ChallengeModel(user, registration);
|
|
||||||
}
|
|
||||||
Enabled = provider?.Enabled ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TwoFactorU2fResponseModel(User user)
|
public TwoFactorU2fResponseModel(User user)
|
||||||
: base("twoFactorU2f")
|
: base("twoFactorU2f")
|
||||||
{
|
{
|
||||||
@ -31,11 +18,27 @@ namespace Bit.Core.Models.Api
|
|||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||||
Enabled = provider != null && provider.Enabled;
|
Enabled = provider?.Enabled ?? false;
|
||||||
|
Keys = provider?.MetaData?.Select(k => new KeyModel(k.Key,
|
||||||
|
new TwoFactorProvider.U2fMetaData((dynamic)k.Value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChallengeModel Challenge { get; set; }
|
|
||||||
public bool Enabled { get; set; }
|
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 class ChallengeModel
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,7 @@ namespace Bit.Core.Models
|
|||||||
|
|
||||||
public U2fMetaData(dynamic o)
|
public U2fMetaData(dynamic o)
|
||||||
{
|
{
|
||||||
|
Name = o.Name;
|
||||||
KeyHandle = o.KeyHandle;
|
KeyHandle = o.KeyHandle;
|
||||||
PublicKey = o.PublicKey;
|
PublicKey = o.PublicKey;
|
||||||
Certificate = o.Certificate;
|
Certificate = o.Certificate;
|
||||||
@ -24,6 +25,7 @@ namespace Bit.Core.Models
|
|||||||
Compromised = o.Compromised;
|
Compromised = o.Compromised;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
public string KeyHandle { get; set; }
|
public string KeyHandle { get; set; }
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public byte[] KeyHandleBytes =>
|
public byte[] KeyHandleBytes =>
|
||||||
|
@ -23,7 +23,8 @@ namespace Bit.Core.Services
|
|||||||
Task SendTwoFactorEmailAsync(User user);
|
Task SendTwoFactorEmailAsync(User user);
|
||||||
Task<bool> VerifyTwoFactorEmailAsync(User user, string token);
|
Task<bool> VerifyTwoFactorEmailAsync(User user, string token);
|
||||||
Task<U2fRegistration> StartU2fRegistrationAsync(User user);
|
Task<U2fRegistration> StartU2fRegistrationAsync(User user);
|
||||||
Task<bool> CompleteU2fRegistrationAsync(User user, string deviceResponse);
|
Task<bool> DeleteU2fKeyAsync(User user, int id);
|
||||||
|
Task<bool> CompleteU2fRegistrationAsync(User user, int id, string name, string deviceResponse);
|
||||||
Task SendEmailVerificationAsync(User user);
|
Task SendEmailVerificationAsync(User user);
|
||||||
Task<IdentityResult> ConfirmEmailAsync(User user, string token);
|
Task<IdentityResult> ConfirmEmailAsync(User user, string token);
|
||||||
Task InitiateEmailChangeAsync(User user, string newEmail);
|
Task InitiateEmailChangeAsync(User user, string newEmail);
|
||||||
|
@ -307,7 +307,7 @@ namespace Bit.Core.Services
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> CompleteU2fRegistrationAsync(User user, string deviceResponse)
|
public async Task<bool> CompleteU2fRegistrationAsync(User user, int id, string name, string deviceResponse)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(deviceResponse))
|
if(string.IsNullOrWhiteSpace(deviceResponse))
|
||||||
{
|
{
|
||||||
@ -323,8 +323,8 @@ namespace Bit.Core.Services
|
|||||||
var registerResponse = BaseModel.FromJson<RegisterResponse>(deviceResponse);
|
var registerResponse = BaseModel.FromJson<RegisterResponse>(deviceResponse);
|
||||||
|
|
||||||
var challenge = challenges.OrderBy(i => i.Id).Last(i => i.KeyHandle == null);
|
var challenge = challenges.OrderBy(i => i.Id).Last(i => i.KeyHandle == null);
|
||||||
var statedReg = new StartedRegistration(challenge.Challenge, challenge.AppId);
|
var startedReg = new StartedRegistration(challenge.Challenge, challenge.AppId);
|
||||||
var reg = U2fLib.FinishRegistration(statedReg, registerResponse);
|
var reg = U2fLib.FinishRegistration(startedReg, registerResponse);
|
||||||
|
|
||||||
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
||||||
|
|
||||||
@ -334,29 +334,74 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||||
}
|
}
|
||||||
else if(providers.ContainsKey(TwoFactorProviderType.U2f))
|
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.Remove(TwoFactorProviderType.U2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
providers.Add(TwoFactorProviderType.U2f, new TwoFactorProvider
|
providers.Add(TwoFactorProviderType.U2f, provider);
|
||||||
{
|
|
||||||
MetaData = new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
["Key1"] = new TwoFactorProvider.U2fMetaData
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Enabled = true
|
|
||||||
});
|
|
||||||
user.SetTwoFactorProviders(providers);
|
user.SetTwoFactorProviders(providers);
|
||||||
await UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.U2f);
|
await UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.U2f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user