1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-25 12:45:18 +01:00

email token provider

This commit is contained in:
Kyle Spearrin 2018-12-19 22:27:45 -05:00
parent 4a38713c4b
commit 951e8f562e
6 changed files with 121 additions and 51 deletions

View File

@ -11,6 +11,7 @@ using Bit.Core.Enums;
using System.Linq;
using Bit.Core;
using Bit.Core.Repositories;
using Bit.Core.Utilities;
namespace Bit.Api.Controllers
{
@ -91,7 +92,8 @@ namespace Bit.Api.Controllers
var user = await CheckAsync(model.MasterPasswordHash, false);
model.ToUser(user);
if(!await _userManager.VerifyTwoFactorTokenAsync(user, TwoFactorProviderType.Authenticator.ToString(), model.Token))
if(!await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.Authenticator), model.Token))
{
await Task.Delay(2000);
throw new BadRequestException("Token", "Invalid token.");
@ -278,7 +280,8 @@ namespace Bit.Api.Controllers
var user = await CheckAsync(model.MasterPasswordHash, false);
model.ToUser(user);
if(!await _userService.VerifyTwoFactorEmailAsync(user, model.Token))
if(!await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), model.Token))
{
await Task.Delay(2000);
throw new BadRequestException("Token", "Invalid token.");
@ -371,7 +374,8 @@ namespace Bit.Api.Controllers
return;
}
if(!await _userManager.VerifyTwoFactorTokenAsync(user, TwoFactorProviderType.YubiKey.ToString(), value))
if(!await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.YubiKey), value))
{
await Task.Delay(2000);
throw new BadRequestException(name, $"{name} is invalid.");

View File

@ -0,0 +1,86 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Bit.Core.Models.Table;
using Bit.Core.Enums;
using Bit.Core.Services;
using Microsoft.Extensions.DependencyInjection;
using Bit.Core.Models;
namespace Bit.Core.Identity
{
public class EmailTokenProvider : IUserTwoFactorTokenProvider<User>
{
private readonly IServiceProvider _serviceProvider;
public EmailTokenProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
if(!HasProperMetaData(provider))
{
return false;
}
return await _serviceProvider.GetRequiredService<IUserService>().
TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Email, user);
}
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
if(!HasProperMetaData(provider))
{
return null;
}
return Task.FromResult(RedactEmail((string)provider.MetaData["Email"]));
}
public Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
return _serviceProvider.GetRequiredService<IUserService>().VerifyTwoFactorEmailAsync(user, token);
}
private bool HasProperMetaData(TwoFactorProvider provider)
{
return provider?.MetaData != null && provider.MetaData.ContainsKey("Email") &&
!string.IsNullOrWhiteSpace((string)provider.MetaData["Email"]);
}
private static string RedactEmail(string email)
{
var emailParts = email.Split('@');
string shownPart = null;
if(emailParts[0].Length > 2 && emailParts[0].Length <= 4)
{
shownPart = emailParts[0].Substring(0, 1);
}
else if(emailParts[0].Length > 4)
{
shownPart = emailParts[0].Substring(0, 2);
}
else
{
shownPart = string.Empty;
}
string redactedPart = null;
if(emailParts[0].Length > 4)
{
redactedPart = new string('*', emailParts[0].Length - 2);
}
else
{
redactedPart = new string('*', emailParts[0].Length - shownPart.Length);
}
return $"{shownPart}{redactedPart}@{emailParts[1]}";
}
}
}

View File

@ -14,6 +14,7 @@ using System.Linq;
using Bit.Core.Models;
using Bit.Core.Identity;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Core.IdentityServer
{
@ -137,7 +138,7 @@ namespace Bit.Core.IdentityServer
if(sendRememberToken)
{
var token = await _userManager.GenerateTwoFactorTokenAsync(user,
TwoFactorProviderType.Remember.ToString());
CoreHelpers.CustomProviderName(TwoFactorProviderType.Remember));
customResponse.Add("TwoFactorToken", token);
}
@ -274,6 +275,7 @@ namespace Bit.Core.IdentityServer
switch(type)
{
case TwoFactorProviderType.Authenticator:
case TwoFactorProviderType.Email:
case TwoFactorProviderType.Duo:
case TwoFactorProviderType.YubiKey:
case TwoFactorProviderType.U2f:
@ -283,13 +285,8 @@ namespace Bit.Core.IdentityServer
{
return false;
}
return await _userManager.VerifyTwoFactorTokenAsync(user, type.ToString(), token);
case TwoFactorProviderType.Email:
if(!(await _userService.TwoFactorProviderIsEnabledAsync(type, user)))
{
return false;
}
return await _userService.VerifyTwoFactorEmailAsync(user, token);
return await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(type), token);
case TwoFactorProviderType.OrganizationDuo:
if(!organization?.TwoFactorProviderIsEnabled(type) ?? true)
{
@ -316,7 +313,8 @@ namespace Bit.Core.IdentityServer
return null;
}
var token = await _userManager.GenerateTwoFactorTokenAsync(user, type.ToString());
var token = await _userManager.GenerateTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(type));
if(type == TwoFactorProviderType.Duo)
{
return new Dictionary<string, object>
@ -339,7 +337,7 @@ namespace Bit.Core.IdentityServer
{
return new Dictionary<string, object>
{
["Email"] = RedactEmail((string)provider.MetaData["Email"])
["Email"] = token
};
}
else if(type == TwoFactorProviderType.YubiKey)
@ -365,37 +363,6 @@ namespace Bit.Core.IdentityServer
}
}
private static string RedactEmail(string email)
{
var emailParts = email.Split('@');
string shownPart = null;
if(emailParts[0].Length > 2 && emailParts[0].Length <= 4)
{
shownPart = emailParts[0].Substring(0, 1);
}
else if(emailParts[0].Length > 4)
{
shownPart = emailParts[0].Substring(0, 2);
}
else
{
shownPart = string.Empty;
}
string redactedPart = null;
if(emailParts[0].Length > 4)
{
redactedPart = new string('*', emailParts[0].Length - 2);
}
else
{
redactedPart = new string('*', emailParts[0].Length - shownPart.Length);
}
return $"{shownPart}{redactedPart}@{emailParts[1]}";
}
private async Task<Device> SaveDeviceAsync(User user, ResourceOwnerPasswordValidationContext context)
{
var device = GetDeviceFromRequest(context);

View File

@ -844,7 +844,7 @@ namespace Bit.Core.Services
public async Task UpdateTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type)
{
if(!type.ToString().StartsWith("Organization"))
if(!type.ToString().Contains("Organization"))
{
throw new ArgumentException("Not an organization provider type.");
}
@ -867,7 +867,7 @@ namespace Bit.Core.Services
public async Task DisableTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type)
{
if(!type.ToString().StartsWith("Organization"))
if(!type.ToString().Contains("Organization"))
{
throw new ArgumentException("Not an organization provider type.");
}

View File

@ -14,6 +14,7 @@ using Dapper;
using System.Globalization;
using System.Web;
using Microsoft.AspNetCore.DataProtection;
using Bit.Core.Enums;
namespace Bit.Core.Utilities
{
@ -474,5 +475,10 @@ namespace Bit.Core.Utilities
return !invalid;
}
public static string CustomProviderName(TwoFactorProviderType type)
{
return string.Concat("Custom_", type.ToString());
}
}
}

View File

@ -189,11 +189,18 @@ namespace Bit.Core.Utilities
.AddUserStore<UserStore>()
.AddRoleStore<RoleStore>()
.AddTokenProvider<DataProtectorTokenProvider<User>>(TokenOptions.DefaultProvider)
.AddTokenProvider<AuthenticatorTokenProvider>(TwoFactorProviderType.Authenticator.ToString())
.AddTokenProvider<YubicoOtpTokenProvider>(TwoFactorProviderType.YubiKey.ToString())
.AddTokenProvider<DuoWebTokenProvider>(TwoFactorProviderType.Duo.ToString())
.AddTokenProvider<U2fTokenProvider>(TwoFactorProviderType.U2f.ToString())
.AddTokenProvider<TwoFactorRememberTokenProvider>(TwoFactorProviderType.Remember.ToString())
.AddTokenProvider<AuthenticatorTokenProvider>(
CoreHelpers.CustomProviderName(TwoFactorProviderType.Authenticator))
.AddTokenProvider<EmailTokenProvider>(
CoreHelpers.CustomProviderName(TwoFactorProviderType.Email))
.AddTokenProvider<YubicoOtpTokenProvider>(
CoreHelpers.CustomProviderName(TwoFactorProviderType.YubiKey))
.AddTokenProvider<DuoWebTokenProvider>(
CoreHelpers.CustomProviderName(TwoFactorProviderType.Duo))
.AddTokenProvider<U2fTokenProvider>(
CoreHelpers.CustomProviderName(TwoFactorProviderType.U2f))
.AddTokenProvider<TwoFactorRememberTokenProvider>(
CoreHelpers.CustomProviderName(TwoFactorProviderType.Remember))
.AddTokenProvider<EmailTokenProvider<User>>(TokenOptions.DefaultEmailProvider);
return identityBuilder;