1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-16 18:48:16 -05: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; 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")] [HttpPost("get-email")]
public async Task<TwoFactorEmailResponseModel> GetEmail([FromBody]TwoFactorRequestModel model) public async Task<TwoFactorEmailResponseModel> GetEmail([FromBody]TwoFactorRequestModel model)
{ {

View File

@ -32,7 +32,7 @@ namespace Bit.Core.Identity
public Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user) 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); return Task.FromResult(false);
} }
@ -45,7 +45,7 @@ namespace Bit.Core.Identity
return Task.FromResult(false); 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); var response = client.Verify(token);
return Task.FromResult(response.Status == YubicoResponseStatus.Ok); 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 Key4 { get; set; }
public string Key5 { 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) public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{ {
if(string.IsNullOrWhiteSpace(Key1) && string.IsNullOrWhiteSpace(Key2) && string.IsNullOrWhiteSpace(Key3) && if(string.IsNullOrWhiteSpace(Key1) && string.IsNullOrWhiteSpace(Key2) && string.IsNullOrWhiteSpace(Key3) &&
string.IsNullOrWhiteSpace(Key4) && string.IsNullOrWhiteSpace(Key5)) 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)); 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) if(provider?.MetaData != null && provider.MetaData.Count > 0)
{ {
Enabled = provider.Enabled; Enabled = provider.Enabled;
@ -29,7 +29,7 @@ namespace Bit.Core.Models.Api
} }
if(provider.MetaData.ContainsKey("Key3")) if(provider.MetaData.ContainsKey("Key3"))
{ {
Key1 = provider.MetaData["Key3"]; Key3 = provider.MetaData["Key3"];
} }
if(provider.MetaData.ContainsKey("Key4")) if(provider.MetaData.ContainsKey("Key4"))
{ {