From 59b8438a0fd48e4fcb90d3b9d51d4a582e13b2ac Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Sat, 24 Jun 2017 09:20:12 -0400 Subject: [PATCH] update u2f lib. send 2fa login email --- src/Api/Controllers/TwoFactorController.cs | 18 ++++++++++++++++++ src/Core/Core.csproj | 2 +- src/Core/Identity/U2fTokenProvider.cs | 13 ++++++------- .../ResourceOwnerPasswordValidator.cs | 15 ++++++++++++++- src/Core/Models/TwoFactorProvider.cs | 2 +- .../Services/Implementations/UserService.cs | 13 ++++++------- 6 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/Api/Controllers/TwoFactorController.cs b/src/Api/Controllers/TwoFactorController.cs index 3e7ee360d..08cd3d512 100644 --- a/src/Api/Controllers/TwoFactorController.cs +++ b/src/Api/Controllers/TwoFactorController.cs @@ -207,6 +207,24 @@ namespace Bit.Api.Controllers await _userService.SendTwoFactorEmailAsync(user); } + [AllowAnonymous] + [HttpPost("send-email-login")] + public async Task SendEmailLogin([FromBody]TwoFactorEmailRequestModel model) + { + var user = await _userManager.FindByEmailAsync(model.Email.ToLowerInvariant()); + if(user != null) + { + if(await _userManager.CheckPasswordAsync(user, model.MasterPasswordHash)) + { + await _userService.SendTwoFactorEmailAsync(user); + return; + } + } + + await Task.Delay(2000); + throw new BadRequestException("Cannot send two-factor email."); + } + [HttpPut("email")] [HttpPost("email")] public async Task PutEmail([FromBody]UpdateTwoFactorEmailRequestModel model) diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 55aef19ab..8bcf8e837 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -56,7 +56,7 @@ - + diff --git a/src/Core/Identity/U2fTokenProvider.cs b/src/Core/Identity/U2fTokenProvider.cs index 340a32041..1e72edb3a 100644 --- a/src/Core/Identity/U2fTokenProvider.cs +++ b/src/Core/Identity/U2fTokenProvider.cs @@ -7,10 +7,9 @@ using Bit.Core.Repositories; using Newtonsoft.Json; using System.Collections.Generic; using System.Linq; -using u2flib.Data; -using u2flib; -using u2flib.Data.Messages; -using u2flib.Exceptions; +using U2fLib = U2F.Core.Crypto.U2F; +using U2F.Core.Models; +using U2F.Core.Exceptions; namespace Bit.Core.Identity { @@ -62,7 +61,7 @@ namespace Bit.Core.Identity { var registration = new DeviceRegistration(key.KeyHandleBytes, key.PublicKeyBytes, key.CertificateBytes, key.Counter); - var auth = U2F.StartAuthentication(Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings), registration); + var auth = U2fLib.StartAuthentication(Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings), registration); // Maybe move this to a bulk create when we support more than 1 key? await _u2fRepository.CreateAsync(new U2f @@ -113,7 +112,7 @@ namespace Bit.Core.Identity return false; } - var authenticateResponse = DataObject.FromJson(token); + var authenticateResponse = BaseModel.FromJson(token); var key = keys.FirstOrDefault(f => f.KeyHandle == authenticateResponse.KeyHandle); if(key == null) @@ -141,7 +140,7 @@ namespace Bit.Core.Identity try { var auth = new StartedAuthentication(challenge.Challenge, challenge.AppId, challenge.KeyHandle); - U2F.FinishAuthentication(auth, authenticateResponse, registration); + U2fLib.FinishAuthentication(auth, authenticateResponse, registration); } catch(U2fException) { diff --git a/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs b/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs index 836f1c7c9..1ccc04355 100644 --- a/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs +++ b/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs @@ -120,7 +120,14 @@ namespace Bit.Core.IdentityServer { var providerKeys = new List(); var providers = new Dictionary>(); - foreach(var provider in user.GetTwoFactorProviders().Where(p => p.Value.Enabled)) + var enabledProviders = user.GetTwoFactorProviders()?.Where(p => p.Value.Enabled); + if(enabledProviders == null) + { + BuildErrorResult(false, context); + return; + } + + foreach(var provider in enabledProviders) { providerKeys.Add((byte)provider.Key); var infoDict = await BuildTwoFactorParams(user, provider.Key, provider.Value); @@ -133,6 +140,12 @@ namespace Bit.Core.IdentityServer { "TwoFactorProviders", providers.Keys }, { "TwoFactorProviders2", providers } }); + + if(enabledProviders.Count() == 1 && enabledProviders.First().Key == TwoFactorProviderType.Email) + { + // Send email now if this is their only 2FA method + await _userService.SendTwoFactorEmailAsync(user); + } } private void BuildErrorResult(bool twoFactorRequest, ResourceOwnerPasswordValidationContext context) diff --git a/src/Core/Models/TwoFactorProvider.cs b/src/Core/Models/TwoFactorProvider.cs index 1f8e80967..1b74bc2a5 100644 --- a/src/Core/Models/TwoFactorProvider.cs +++ b/src/Core/Models/TwoFactorProvider.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; -using u2flib.Util; +using U2F.Core.Utils; namespace Bit.Core.Models { diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index 58a1e7fe5..27b4c1748 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -12,10 +12,9 @@ using Bit.Core.Enums; using System.Security.Claims; using Bit.Core.Models; using Bit.Core.Models.Business; -using u2flib.Data.Messages; -using u2flib.Util; -using u2flib; -using u2flib.Data; +using U2fLib = U2F.Core.Crypto.U2F; +using U2F.Core.Models; +using U2F.Core.Utils; namespace Bit.Core.Services { @@ -220,7 +219,7 @@ namespace Bit.Core.Services public async Task StartU2fRegistrationAsync(User user) { await _u2fRepository.DeleteManyByUserIdAsync(user.Id); - var reg = U2F.StartRegistration(Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings)); + var reg = U2fLib.StartRegistration(Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings)); await _u2fRepository.CreateAsync(new U2f { AppId = reg.AppId, @@ -250,11 +249,11 @@ namespace Bit.Core.Services return false; } - var registerResponse = DataObject.FromJson(deviceResponse); + var registerResponse = BaseModel.FromJson(deviceResponse); var challenge = challenges.OrderBy(i => i.Id).Last(i => i.KeyHandle == null); var statedReg = new StartedRegistration(challenge.Challenge, challenge.AppId); - var reg = U2F.FinishRegistration(statedReg, registerResponse); + var reg = U2fLib.FinishRegistration(statedReg, registerResponse); await _u2fRepository.DeleteManyByUserIdAsync(user.Id);