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:
parent
4a38713c4b
commit
951e8f562e
@ -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.");
|
||||
|
86
src/Core/Identity/EmailTokenProvider.cs
Normal file
86
src/Core/Identity/EmailTokenProvider.cs
Normal 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]}";
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user