1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-22 12:15:36 +01:00

yubikey setup for 2FA

This commit is contained in:
Kyle Spearrin 2017-06-20 14:12:31 -04:00
parent 612697e815
commit 69de88cc32
4 changed files with 115 additions and 7 deletions

View File

@ -67,6 +67,51 @@ namespace Bit.Api.Controllers
return response;
}
[HttpPost("get-yubikey")]
public async Task<TwoFactorYubiKeyResponseModel> GetYubiKey([FromBody]TwoFactorRequestModel model)
{
var user = await CheckPasswordAsync(model.MasterPasswordHash);
var response = new TwoFactorYubiKeyResponseModel(user);
return response;
}
[HttpPut("yubikey")]
[HttpPost("yubikey")]
public async Task<TwoFactorYubiKeyResponseModel> PutYubiKey(
[FromBody]UpdateTwoFactorYubicoOtpRequestModel model)
{
var user = await CheckPasswordAsync(model.MasterPasswordHash);
model.ToUser(user);
await ValidateYubiKeyAsync(user, nameof(model.Key1), model.Key1);
await ValidateYubiKeyAsync(user, nameof(model.Key2), model.Key2);
await ValidateYubiKeyAsync(user, nameof(model.Key3), model.Key3);
await ValidateYubiKeyAsync(user, nameof(model.Key4), model.Key4);
await ValidateYubiKeyAsync(user, nameof(model.Key5), model.Key5);
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.YubiKey);
var response = new TwoFactorYubiKeyResponseModel(user);
return response;
}
public async Task ValidateYubiKeyAsync(User user, string name, string value)
{
if(string.IsNullOrWhiteSpace(value) || value.Length == 12)
{
return;
}
if(!await _userManager.VerifyTwoFactorTokenAsync(user, TwoFactorProviderType.YubiKey.ToString(), value))
{
await Task.Delay(2000);
throw new BadRequestException(name, $"{name} is invalid.");
}
else
{
await Task.Delay(500);
}
}
[HttpPost("get-email")]
public async Task<TwoFactorEmailResponseModel> GetEmail([FromBody]TwoFactorRequestModel model)
{
@ -74,7 +119,7 @@ namespace Bit.Api.Controllers
var response = new TwoFactorEmailResponseModel(user);
return response;
}
[HttpPost("send-email")]
public async Task SendEmail([FromBody]TwoFactorEmailRequestModel model)
{
@ -136,7 +181,7 @@ namespace Bit.Api.Controllers
await Task.Delay(2000);
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
}
return user;
}
}

View File

@ -32,7 +32,7 @@ namespace Bit.Core.Identity
public Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
if(token.Length != 44)
if(string.IsNullOrWhiteSpace(token) || token.Length != 44)
{
return Task.FromResult(false);
}
@ -45,7 +45,7 @@ namespace Bit.Core.Identity
return Task.FromResult(false);
}
var client = new YubicoClient(_globalSettings.Yubico.ClientId, _globalSettings.Yubico.ClientId);
var client = new YubicoClient(_globalSettings.Yubico.ClientId, _globalSettings.Yubico.Key);
var response = client.Verify(token);
return Task.FromResult(response.Status == YubicoResponseStatus.Ok);
}

View File

@ -57,12 +57,75 @@ namespace Bit.Core.Models.Api
public string Key4 { get; set; }
public string Key5 { get; set; }
public User ToUser(User extistingUser)
{
var providers = extistingUser.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, string>
{
["Key1"] = FormatKey(Key1),
["Key2"] = FormatKey(Key2),
["Key3"] = FormatKey(Key3),
["Key4"] = FormatKey(Key4),
["Key5"] = FormatKey(Key5)
},
Enabled = true
});
extistingUser.SetTwoFactorProviders(providers);
return extistingUser;
}
private string FormatKey(string keyValue)
{
if(string.IsNullOrWhiteSpace(keyValue))
{
return null;
}
return keyValue.Substring(0, 12);
}
public 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) });
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) });
}
}
}

View File

@ -14,7 +14,7 @@ namespace Bit.Core.Models.Api
throw new ArgumentNullException(nameof(user));
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
if(provider?.MetaData != null && provider.MetaData.Count > 0)
{
Enabled = provider.Enabled;
@ -29,7 +29,7 @@ namespace Bit.Core.Models.Api
}
if(provider.MetaData.ContainsKey("Key3"))
{
Key1 = provider.MetaData["Key3"];
Key3 = provider.MetaData["Key3"];
}
if(provider.MetaData.ContainsKey("Key4"))
{