1
0
mirror of https://github.com/bitwarden/server synced 2025-12-25 04:33:26 +00:00

Revert filescoped (#2227)

* Revert "Add git blame entry (#2226)"

This reverts commit 239286737d.

* Revert "Turn on file scoped namespaces (#2225)"

This reverts commit 34fb4cca2a.
This commit is contained in:
Justin Baur
2022-08-29 15:53:48 -04:00
committed by GitHub
parent 239286737d
commit bae03feffe
1208 changed files with 74317 additions and 73126 deletions

View File

@@ -5,41 +5,42 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using OtpNet;
namespace Bit.Core.Identity;
public class AuthenticatorTokenProvider : IUserTwoFactorTokenProvider<User>
namespace Bit.Core.Identity
{
private readonly IServiceProvider _serviceProvider;
public AuthenticatorTokenProvider(IServiceProvider serviceProvider)
public class AuthenticatorTokenProvider : IUserTwoFactorTokenProvider<User>
{
_serviceProvider = serviceProvider;
}
private readonly IServiceProvider _serviceProvider;
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
if (string.IsNullOrWhiteSpace((string)provider?.MetaData["Key"]))
public AuthenticatorTokenProvider(IServiceProvider serviceProvider)
{
return false;
_serviceProvider = serviceProvider;
}
return await _serviceProvider.GetRequiredService<IUserService>()
.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Authenticator, user);
}
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
return Task.FromResult<string>(null);
}
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
if (string.IsNullOrWhiteSpace((string)provider?.MetaData["Key"]))
{
return false;
}
return await _serviceProvider.GetRequiredService<IUserService>()
.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Authenticator, user);
}
public Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
var otp = new Totp(Base32Encoding.ToBytes((string)provider.MetaData["Key"]));
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
return Task.FromResult<string>(null);
}
long timeStepMatched;
var valid = otp.VerifyTotp(token, out timeStepMatched, new VerificationWindow(1, 1));
public Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
var otp = new Totp(Base32Encoding.ToBytes((string)provider.MetaData["Key"]));
return Task.FromResult(valid);
long timeStepMatched;
var valid = otp.VerifyTotp(token, out timeStepMatched, new VerificationWindow(1, 1));
return Task.FromResult(valid);
}
}
}

View File

@@ -2,48 +2,49 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection;
// ref: https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNetCore.Identity/IdentityServiceCollectionExtensions.cs
public static class CustomIdentityServiceCollectionExtensions
namespace Microsoft.Extensions.DependencyInjection
{
public static IdentityBuilder AddIdentityWithoutCookieAuth<TUser, TRole>(
this IServiceCollection services)
where TUser : class
where TRole : class
// ref: https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNetCore.Identity/IdentityServiceCollectionExtensions.cs
public static class CustomIdentityServiceCollectionExtensions
{
return services.AddIdentityWithoutCookieAuth<TUser, TRole>(setupAction: null);
}
public static IdentityBuilder AddIdentityWithoutCookieAuth<TUser, TRole>(
this IServiceCollection services,
Action<IdentityOptions> setupAction)
where TUser : class
where TRole : class
{
// Hosting doesn't add IHttpContextAccessor by default
services.AddHttpContextAccessor();
// Identity services
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
services.TryAddScoped<ILookupNormalizer, LowerInvariantLookupNormalizer>();
services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
// No interface for the error describer so we can add errors without rev'ing the interface
services.TryAddScoped<IdentityErrorDescriber>();
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
services.TryAddScoped<IUserConfirmation<TUser>, DefaultUserConfirmation<TUser>>();
services.TryAddScoped<UserManager<TUser>>();
services.TryAddScoped<SignInManager<TUser>>();
services.TryAddScoped<RoleManager<TRole>>();
if (setupAction != null)
public static IdentityBuilder AddIdentityWithoutCookieAuth<TUser, TRole>(
this IServiceCollection services)
where TUser : class
where TRole : class
{
services.Configure(setupAction);
return services.AddIdentityWithoutCookieAuth<TUser, TRole>(setupAction: null);
}
return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
public static IdentityBuilder AddIdentityWithoutCookieAuth<TUser, TRole>(
this IServiceCollection services,
Action<IdentityOptions> setupAction)
where TUser : class
where TRole : class
{
// Hosting doesn't add IHttpContextAccessor by default
services.AddHttpContextAccessor();
// Identity services
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
services.TryAddScoped<ILookupNormalizer, LowerInvariantLookupNormalizer>();
services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
// No interface for the error describer so we can add errors without rev'ing the interface
services.TryAddScoped<IdentityErrorDescriber>();
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
services.TryAddScoped<IUserConfirmation<TUser>, DefaultUserConfirmation<TUser>>();
services.TryAddScoped<UserManager<TUser>>();
services.TryAddScoped<SignInManager<TUser>>();
services.TryAddScoped<RoleManager<TRole>>();
if (setupAction != null)
{
services.Configure(setupAction);
}
return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
}
}
}

View File

@@ -7,80 +7,81 @@ using Bit.Core.Utilities.Duo;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Identity;
public class DuoWebTokenProvider : IUserTwoFactorTokenProvider<User>
namespace Bit.Core.Identity
{
private readonly IServiceProvider _serviceProvider;
private readonly GlobalSettings _globalSettings;
public DuoWebTokenProvider(
IServiceProvider serviceProvider,
GlobalSettings globalSettings)
public class DuoWebTokenProvider : IUserTwoFactorTokenProvider<User>
{
_serviceProvider = serviceProvider;
_globalSettings = globalSettings;
}
private readonly IServiceProvider _serviceProvider;
private readonly GlobalSettings _globalSettings;
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
public DuoWebTokenProvider(
IServiceProvider serviceProvider,
GlobalSettings globalSettings)
{
return false;
_serviceProvider = serviceProvider;
_globalSettings = globalSettings;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
if (!HasProperMetaData(provider))
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
return false;
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
{
return false;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
if (!HasProperMetaData(provider))
{
return false;
}
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Duo, user);
}
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Duo, user);
}
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
return null;
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
{
return null;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
if (!HasProperMetaData(provider))
{
return null;
}
var signatureRequest = DuoWeb.SignRequest((string)provider.MetaData["IKey"],
(string)provider.MetaData["SKey"], _globalSettings.Duo.AKey, user.Email);
return signatureRequest;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
if (!HasProperMetaData(provider))
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
return null;
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
{
return false;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
if (!HasProperMetaData(provider))
{
return false;
}
var response = DuoWeb.VerifyResponse((string)provider.MetaData["IKey"], (string)provider.MetaData["SKey"],
_globalSettings.Duo.AKey, token);
return response == user.Email;
}
var signatureRequest = DuoWeb.SignRequest((string)provider.MetaData["IKey"],
(string)provider.MetaData["SKey"], _globalSettings.Duo.AKey, user.Email);
return signatureRequest;
}
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
private bool HasProperMetaData(TwoFactorProvider provider)
{
return false;
return provider?.MetaData != null && provider.MetaData.ContainsKey("IKey") &&
provider.MetaData.ContainsKey("SKey") && provider.MetaData.ContainsKey("Host");
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
if (!HasProperMetaData(provider))
{
return false;
}
var response = DuoWeb.VerifyResponse((string)provider.MetaData["IKey"], (string)provider.MetaData["SKey"],
_globalSettings.Duo.AKey, token);
return response == user.Email;
}
private bool HasProperMetaData(TwoFactorProvider provider)
{
return provider?.MetaData != null && provider.MetaData.ContainsKey("IKey") &&
provider.MetaData.ContainsKey("SKey") && provider.MetaData.ContainsKey("Host");
}
}

View File

@@ -5,79 +5,80 @@ using Bit.Core.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Identity;
public class EmailTokenProvider : IUserTwoFactorTokenProvider<User>
namespace Bit.Core.Identity
{
private readonly IServiceProvider _serviceProvider;
public EmailTokenProvider(IServiceProvider serviceProvider)
public class EmailTokenProvider : IUserTwoFactorTokenProvider<User>
{
_serviceProvider = serviceProvider;
}
private readonly IServiceProvider _serviceProvider;
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
if (!HasProperMetaData(provider))
public EmailTokenProvider(IServiceProvider serviceProvider)
{
return false;
_serviceProvider = serviceProvider;
}
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))
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
return null;
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
if (!HasProperMetaData(provider))
{
return false;
}
return await _serviceProvider.GetRequiredService<IUserService>().
TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.Email, user);
}
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)
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
shownPart = emailParts[0].Substring(0, 1);
}
else if (emailParts[0].Length > 4)
{
shownPart = emailParts[0].Substring(0, 2);
}
else
{
shownPart = string.Empty;
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
if (!HasProperMetaData(provider))
{
return null;
}
return Task.FromResult(RedactEmail((string)provider.MetaData["Email"]));
}
string redactedPart = null;
if (emailParts[0].Length > 4)
public Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
redactedPart = new string('*', emailParts[0].Length - 2);
}
else
{
redactedPart = new string('*', emailParts[0].Length - shownPart.Length);
return _serviceProvider.GetRequiredService<IUserService>().VerifyTwoFactorEmailAsync(user, token);
}
return $"{shownPart}{redactedPart}@{emailParts[1]}";
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

@@ -1,10 +1,11 @@
using Bit.Core.Entities;
namespace Bit.Core.Identity;
public interface IOrganizationTwoFactorTokenProvider
namespace Bit.Core.Identity
{
Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization);
Task<string> GenerateAsync(Organization organization, User user);
Task<bool> ValidateAsync(string token, Organization organization, User user);
public interface IOrganizationTwoFactorTokenProvider
{
Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization);
Task<string> GenerateAsync(Organization organization, User user);
Task<bool> ValidateAsync(string token, Organization organization, User user);
}
}

View File

@@ -1,21 +1,22 @@
using Microsoft.AspNetCore.Identity;
namespace Bit.Core.Identity;
public class LowerInvariantLookupNormalizer : ILookupNormalizer
namespace Bit.Core.Identity
{
public string NormalizeEmail(string email)
public class LowerInvariantLookupNormalizer : ILookupNormalizer
{
return Normalize(email);
}
public string NormalizeEmail(string email)
{
return Normalize(email);
}
public string NormalizeName(string name)
{
return Normalize(name);
}
public string NormalizeName(string name)
{
return Normalize(name);
}
private string Normalize(string key)
{
return key?.Normalize().ToLowerInvariant();
private string Normalize(string key)
{
return key?.Normalize().ToLowerInvariant();
}
}
}

View File

@@ -4,72 +4,73 @@ using Bit.Core.Models;
using Bit.Core.Settings;
using Bit.Core.Utilities.Duo;
namespace Bit.Core.Identity;
public interface IOrganizationDuoWebTokenProvider : IOrganizationTwoFactorTokenProvider { }
public class OrganizationDuoWebTokenProvider : IOrganizationDuoWebTokenProvider
namespace Bit.Core.Identity
{
private readonly GlobalSettings _globalSettings;
public interface IOrganizationDuoWebTokenProvider : IOrganizationTwoFactorTokenProvider { }
public OrganizationDuoWebTokenProvider(GlobalSettings globalSettings)
public class OrganizationDuoWebTokenProvider : IOrganizationDuoWebTokenProvider
{
_globalSettings = globalSettings;
}
private readonly GlobalSettings _globalSettings;
public Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization)
{
if (organization == null || !organization.Enabled || !organization.Use2fa)
public OrganizationDuoWebTokenProvider(GlobalSettings globalSettings)
{
return Task.FromResult(false);
_globalSettings = globalSettings;
}
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
var canGenerate = organization.TwoFactorProviderIsEnabled(TwoFactorProviderType.OrganizationDuo)
&& HasProperMetaData(provider);
return Task.FromResult(canGenerate);
}
public Task<string> GenerateAsync(Organization organization, User user)
{
if (organization == null || !organization.Enabled || !organization.Use2fa)
public Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization)
{
return Task.FromResult<string>(null);
if (organization == null || !organization.Enabled || !organization.Use2fa)
{
return Task.FromResult(false);
}
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
var canGenerate = organization.TwoFactorProviderIsEnabled(TwoFactorProviderType.OrganizationDuo)
&& HasProperMetaData(provider);
return Task.FromResult(canGenerate);
}
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
if (!HasProperMetaData(provider))
public Task<string> GenerateAsync(Organization organization, User user)
{
return Task.FromResult<string>(null);
if (organization == null || !organization.Enabled || !organization.Use2fa)
{
return Task.FromResult<string>(null);
}
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
if (!HasProperMetaData(provider))
{
return Task.FromResult<string>(null);
}
var signatureRequest = DuoWeb.SignRequest(provider.MetaData["IKey"].ToString(),
provider.MetaData["SKey"].ToString(), _globalSettings.Duo.AKey, user.Email);
return Task.FromResult(signatureRequest);
}
var signatureRequest = DuoWeb.SignRequest(provider.MetaData["IKey"].ToString(),
provider.MetaData["SKey"].ToString(), _globalSettings.Duo.AKey, user.Email);
return Task.FromResult(signatureRequest);
}
public Task<bool> ValidateAsync(string token, Organization organization, User user)
{
if (organization == null || !organization.Enabled || !organization.Use2fa)
public Task<bool> ValidateAsync(string token, Organization organization, User user)
{
return Task.FromResult(false);
if (organization == null || !organization.Enabled || !organization.Use2fa)
{
return Task.FromResult(false);
}
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
if (!HasProperMetaData(provider))
{
return Task.FromResult(false);
}
var response = DuoWeb.VerifyResponse(provider.MetaData["IKey"].ToString(),
provider.MetaData["SKey"].ToString(), _globalSettings.Duo.AKey, token);
return Task.FromResult(response == user.Email);
}
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
if (!HasProperMetaData(provider))
private bool HasProperMetaData(TwoFactorProvider provider)
{
return Task.FromResult(false);
return provider?.MetaData != null && provider.MetaData.ContainsKey("IKey") &&
provider.MetaData.ContainsKey("SKey") && provider.MetaData.ContainsKey("Host");
}
var response = DuoWeb.VerifyResponse(provider.MetaData["IKey"].ToString(),
provider.MetaData["SKey"].ToString(), _globalSettings.Duo.AKey, token);
return Task.FromResult(response == user.Email);
}
private bool HasProperMetaData(TwoFactorProvider provider)
{
return provider?.MetaData != null && provider.MetaData.ContainsKey("IKey") &&
provider.MetaData.ContainsKey("SKey") && provider.MetaData.ContainsKey("Host");
}
}

View File

@@ -5,85 +5,86 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Bit.Core.Identity;
public class PasswordlessSignInManager<TUser> : SignInManager<TUser> where TUser : class
namespace Bit.Core.Identity
{
public const string PasswordlessSignInPurpose = "PasswordlessSignIn";
private readonly IMailService _mailService;
public PasswordlessSignInManager(UserManager<TUser> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<TUser> claimsFactory,
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<TUser>> logger,
IAuthenticationSchemeProvider schemes,
IUserConfirmation<TUser> confirmation,
IMailService mailService)
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
public class PasswordlessSignInManager<TUser> : SignInManager<TUser> where TUser : class
{
_mailService = mailService;
}
public const string PasswordlessSignInPurpose = "PasswordlessSignIn";
public async Task<SignInResult> PasswordlessSignInAsync(string email, string returnUrl)
{
var user = await UserManager.FindByEmailAsync(email);
if (user == null)
private readonly IMailService _mailService;
public PasswordlessSignInManager(UserManager<TUser> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<TUser> claimsFactory,
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<TUser>> logger,
IAuthenticationSchemeProvider schemes,
IUserConfirmation<TUser> confirmation,
IMailService mailService)
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
{
return SignInResult.Failed;
_mailService = mailService;
}
var token = await UserManager.GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
PasswordlessSignInPurpose);
await _mailService.SendPasswordlessSignInAsync(returnUrl, token, email);
return SignInResult.Success;
}
public async Task<SignInResult> PasswordlessSignInAsync(TUser user, string token, bool isPersistent)
{
if (user == null)
public async Task<SignInResult> PasswordlessSignInAsync(string email, string returnUrl)
{
throw new ArgumentNullException(nameof(user));
}
var user = await UserManager.FindByEmailAsync(email);
if (user == null)
{
return SignInResult.Failed;
}
var attempt = await CheckPasswordlessSignInAsync(user, token);
return attempt.Succeeded ?
await SignInOrTwoFactorAsync(user, isPersistent, bypassTwoFactor: true) : attempt;
}
public async Task<SignInResult> PasswordlessSignInAsync(string email, string token, bool isPersistent)
{
var user = await UserManager.FindByEmailAsync(email);
if (user == null)
{
return SignInResult.Failed;
}
return await PasswordlessSignInAsync(user, token, isPersistent);
}
public virtual async Task<SignInResult> CheckPasswordlessSignInAsync(TUser user, string token)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var error = await PreSignInCheck(user);
if (error != null)
{
return error;
}
if (await UserManager.VerifyUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
PasswordlessSignInPurpose, token))
{
var token = await UserManager.GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
PasswordlessSignInPurpose);
await _mailService.SendPasswordlessSignInAsync(returnUrl, token, email);
return SignInResult.Success;
}
Logger.LogWarning(2, "User {userId} failed to provide the correct token.",
await UserManager.GetUserIdAsync(user));
return SignInResult.Failed;
public async Task<SignInResult> PasswordlessSignInAsync(TUser user, string token, bool isPersistent)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var attempt = await CheckPasswordlessSignInAsync(user, token);
return attempt.Succeeded ?
await SignInOrTwoFactorAsync(user, isPersistent, bypassTwoFactor: true) : attempt;
}
public async Task<SignInResult> PasswordlessSignInAsync(string email, string token, bool isPersistent)
{
var user = await UserManager.FindByEmailAsync(email);
if (user == null)
{
return SignInResult.Failed;
}
return await PasswordlessSignInAsync(user, token, isPersistent);
}
public virtual async Task<SignInResult> CheckPasswordlessSignInAsync(TUser user, string token)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var error = await PreSignInCheck(user);
if (error != null)
{
return error;
}
if (await UserManager.VerifyUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
PasswordlessSignInPurpose, token))
{
return SignInResult.Success;
}
Logger.LogWarning(2, "User {userId} failed to provide the correct token.",
await UserManager.GetUserIdAsync(user));
return SignInResult.Failed;
}
}
}

View File

@@ -2,37 +2,38 @@
using Bit.Core.Services;
using Microsoft.AspNetCore.Identity;
namespace Bit.Core.Identity;
public class ReadOnlyDatabaseIdentityUserStore : ReadOnlyIdentityUserStore
namespace Bit.Core.Identity
{
private readonly IUserService _userService;
private readonly IUserRepository _userRepository;
public ReadOnlyDatabaseIdentityUserStore(
IUserService userService,
IUserRepository userRepository)
public class ReadOnlyDatabaseIdentityUserStore : ReadOnlyIdentityUserStore
{
_userService = userService;
_userRepository = userRepository;
}
private readonly IUserService _userService;
private readonly IUserRepository _userRepository;
public override async Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
CancellationToken cancellationToken = default(CancellationToken))
{
var user = await _userRepository.GetByEmailAsync(normalizedEmail);
return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user));
}
public override async Task<IdentityUser> FindByIdAsync(string userId,
CancellationToken cancellationToken = default(CancellationToken))
{
if (!Guid.TryParse(userId, out var userIdGuid))
public ReadOnlyDatabaseIdentityUserStore(
IUserService userService,
IUserRepository userRepository)
{
return null;
_userService = userService;
_userRepository = userRepository;
}
var user = await _userRepository.GetByIdAsync(userIdGuid);
return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user));
public override async Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
CancellationToken cancellationToken = default(CancellationToken))
{
var user = await _userRepository.GetByEmailAsync(normalizedEmail);
return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user));
}
public override async Task<IdentityUser> FindByIdAsync(string userId,
CancellationToken cancellationToken = default(CancellationToken))
{
if (!Guid.TryParse(userId, out var userIdGuid))
{
return null;
}
var user = await _userRepository.GetByIdAsync(userIdGuid);
return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user));
}
}
}

View File

@@ -2,65 +2,66 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
namespace Bit.Core.Identity;
public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
namespace Bit.Core.Identity
{
private readonly IConfiguration _configuration;
public ReadOnlyEnvIdentityUserStore(IConfiguration configuration)
public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
{
_configuration = configuration;
}
private readonly IConfiguration _configuration;
public override Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
CancellationToken cancellationToken = default(CancellationToken))
{
var usersCsv = _configuration["adminSettings:admins"];
if (!CoreHelpers.SettingHasValue(usersCsv))
public ReadOnlyEnvIdentityUserStore(IConfiguration configuration)
{
return Task.FromResult<IdentityUser>(null);
_configuration = configuration;
}
var users = usersCsv.ToLowerInvariant().Split(',');
var usersDict = new Dictionary<string, string>();
foreach (var u in users)
public override Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
CancellationToken cancellationToken = default(CancellationToken))
{
var parts = u.Split(':');
if (parts.Length == 2)
var usersCsv = _configuration["adminSettings:admins"];
if (!CoreHelpers.SettingHasValue(usersCsv))
{
var email = parts[0].Trim();
var stamp = parts[1].Trim();
usersDict.Add(email, stamp);
return Task.FromResult<IdentityUser>(null);
}
else
var users = usersCsv.ToLowerInvariant().Split(',');
var usersDict = new Dictionary<string, string>();
foreach (var u in users)
{
var email = parts[0].Trim();
usersDict.Add(email, email);
var parts = u.Split(':');
if (parts.Length == 2)
{
var email = parts[0].Trim();
var stamp = parts[1].Trim();
usersDict.Add(email, stamp);
}
else
{
var email = parts[0].Trim();
usersDict.Add(email, email);
}
}
var userStamp = usersDict.ContainsKey(normalizedEmail) ? usersDict[normalizedEmail] : null;
if (userStamp == null)
{
return Task.FromResult<IdentityUser>(null);
}
return Task.FromResult(new IdentityUser
{
Id = normalizedEmail,
Email = normalizedEmail,
NormalizedEmail = normalizedEmail,
EmailConfirmed = true,
UserName = normalizedEmail,
NormalizedUserName = normalizedEmail,
SecurityStamp = userStamp
});
}
var userStamp = usersDict.ContainsKey(normalizedEmail) ? usersDict[normalizedEmail] : null;
if (userStamp == null)
public override Task<IdentityUser> FindByIdAsync(string userId,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult<IdentityUser>(null);
return FindByEmailAsync(userId, cancellationToken);
}
return Task.FromResult(new IdentityUser
{
Id = normalizedEmail,
Email = normalizedEmail,
NormalizedEmail = normalizedEmail,
EmailConfirmed = true,
UserName = normalizedEmail,
NormalizedUserName = normalizedEmail,
SecurityStamp = userStamp
});
}
public override Task<IdentityUser> FindByIdAsync(string userId,
CancellationToken cancellationToken = default(CancellationToken))
{
return FindByEmailAsync(userId, cancellationToken);
}
}

View File

@@ -1,119 +1,120 @@
using Microsoft.AspNetCore.Identity;
namespace Bit.Core.Identity;
public abstract class ReadOnlyIdentityUserStore :
IUserStore<IdentityUser>,
IUserEmailStore<IdentityUser>,
IUserSecurityStampStore<IdentityUser>
namespace Bit.Core.Identity
{
public void Dispose() { }
public Task<IdentityResult> CreateAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
public abstract class ReadOnlyIdentityUserStore :
IUserStore<IdentityUser>,
IUserEmailStore<IdentityUser>,
IUserSecurityStampStore<IdentityUser>
{
throw new NotImplementedException();
}
public void Dispose() { }
public Task<IdentityResult> DeleteAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<IdentityResult> CreateAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public abstract Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
CancellationToken cancellationToken = default(CancellationToken));
public Task<IdentityResult> DeleteAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public abstract Task<IdentityUser> FindByIdAsync(string userId,
CancellationToken cancellationToken = default(CancellationToken));
public abstract Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
CancellationToken cancellationToken = default(CancellationToken));
public async Task<IdentityUser> FindByNameAsync(string normalizedUserName,
CancellationToken cancellationToken = default(CancellationToken))
{
return await FindByEmailAsync(normalizedUserName, cancellationToken);
}
public abstract Task<IdentityUser> FindByIdAsync(string userId,
CancellationToken cancellationToken = default(CancellationToken));
public Task<string> GetEmailAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public async Task<IdentityUser> FindByNameAsync(string normalizedUserName,
CancellationToken cancellationToken = default(CancellationToken))
{
return await FindByEmailAsync(normalizedUserName, cancellationToken);
}
public Task<bool> GetEmailConfirmedAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.EmailConfirmed);
}
public Task<string> GetEmailAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<string> GetNormalizedEmailAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<bool> GetEmailConfirmedAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.EmailConfirmed);
}
public Task<string> GetNormalizedUserNameAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<string> GetNormalizedEmailAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<string> GetUserIdAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Id);
}
public Task<string> GetNormalizedUserNameAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<string> GetUserNameAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<string> GetUserIdAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Id);
}
public Task SetEmailAsync(IdentityUser user, string email,
CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<string> GetUserNameAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task SetEmailConfirmedAsync(IdentityUser user, bool confirmed,
CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetEmailAsync(IdentityUser user, string email,
CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetNormalizedEmailAsync(IdentityUser user, string normalizedEmail,
CancellationToken cancellationToken = default(CancellationToken))
{
user.NormalizedEmail = normalizedEmail;
return Task.FromResult(0);
}
public Task SetEmailConfirmedAsync(IdentityUser user, bool confirmed,
CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetNormalizedUserNameAsync(IdentityUser user, string normalizedName,
CancellationToken cancellationToken = default(CancellationToken))
{
user.NormalizedUserName = normalizedName;
return Task.FromResult(0);
}
public Task SetNormalizedEmailAsync(IdentityUser user, string normalizedEmail,
CancellationToken cancellationToken = default(CancellationToken))
{
user.NormalizedEmail = normalizedEmail;
return Task.FromResult(0);
}
public Task SetUserNameAsync(IdentityUser user, string userName,
CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetNormalizedUserNameAsync(IdentityUser user, string normalizedName,
CancellationToken cancellationToken = default(CancellationToken))
{
user.NormalizedUserName = normalizedName;
return Task.FromResult(0);
}
public Task<IdentityResult> UpdateAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(IdentityResult.Success);
}
public Task SetUserNameAsync(IdentityUser user, string userName,
CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetSecurityStampAsync(IdentityUser user, string stamp, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> UpdateAsync(IdentityUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(IdentityResult.Success);
}
public Task<string> GetSecurityStampAsync(IdentityUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.SecurityStamp);
public Task SetSecurityStampAsync(IdentityUser user, string stamp, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetSecurityStampAsync(IdentityUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.SecurityStamp);
}
}
}

View File

@@ -1,60 +1,61 @@
using Bit.Core.Entities;
using Microsoft.AspNetCore.Identity;
namespace Bit.Core.Identity;
public class RoleStore : IRoleStore<Role>
namespace Bit.Core.Identity
{
public void Dispose() { }
public Task<IdentityResult> CreateAsync(Role role, CancellationToken cancellationToken)
public class RoleStore : IRoleStore<Role>
{
throw new NotImplementedException();
}
public void Dispose() { }
public Task<IdentityResult> DeleteAsync(Role role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> CreateAsync(Role role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<Role> FindByIdAsync(string roleId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> DeleteAsync(Role role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<Role> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<Role> FindByIdAsync(string roleId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedRoleNameAsync(Role role, CancellationToken cancellationToken)
{
return Task.FromResult(role.Name);
}
public Task<Role> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetRoleIdAsync(Role role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedRoleNameAsync(Role role, CancellationToken cancellationToken)
{
return Task.FromResult(role.Name);
}
public Task<string> GetRoleNameAsync(Role role, CancellationToken cancellationToken)
{
return Task.FromResult(role.Name);
}
public Task<string> GetRoleIdAsync(Role role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetNormalizedRoleNameAsync(Role role, string normalizedName, CancellationToken cancellationToken)
{
return Task.FromResult(0);
}
public Task<string> GetRoleNameAsync(Role role, CancellationToken cancellationToken)
{
return Task.FromResult(role.Name);
}
public Task SetRoleNameAsync(Role role, string roleName, CancellationToken cancellationToken)
{
role.Name = roleName;
return Task.FromResult(0);
}
public Task SetNormalizedRoleNameAsync(Role role, string normalizedName, CancellationToken cancellationToken)
{
return Task.FromResult(0);
}
public Task<IdentityResult> UpdateAsync(Role role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
public Task SetRoleNameAsync(Role role, string roleName, CancellationToken cancellationToken)
{
role.Name = roleName;
return Task.FromResult(0);
}
public Task<IdentityResult> UpdateAsync(Role role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}

View File

@@ -4,17 +4,18 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Bit.Core.Identity;
public class TwoFactorRememberTokenProvider : DataProtectorTokenProvider<User>
namespace Bit.Core.Identity
{
public TwoFactorRememberTokenProvider(
IDataProtectionProvider dataProtectionProvider,
IOptions<TwoFactorRememberTokenProviderOptions> options,
ILogger<DataProtectorTokenProvider<User>> logger)
: base(dataProtectionProvider, options, logger)
public class TwoFactorRememberTokenProvider : DataProtectorTokenProvider<User>
{
public TwoFactorRememberTokenProvider(
IDataProtectionProvider dataProtectionProvider,
IOptions<TwoFactorRememberTokenProviderOptions> options,
ILogger<DataProtectorTokenProvider<User>> logger)
: base(dataProtectionProvider, options, logger)
{ }
}
public class TwoFactorRememberTokenProviderOptions : DataProtectionTokenProviderOptions
{ }
}
public class TwoFactorRememberTokenProviderOptions : DataProtectionTokenProviderOptions
{ }

View File

@@ -5,179 +5,180 @@ using Bit.Core.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Identity;
public class UserStore :
IUserStore<User>,
IUserPasswordStore<User>,
IUserEmailStore<User>,
IUserTwoFactorStore<User>,
IUserSecurityStampStore<User>
namespace Bit.Core.Identity
{
private readonly IServiceProvider _serviceProvider;
private readonly IUserRepository _userRepository;
private readonly ICurrentContext _currentContext;
public UserStore(
IServiceProvider serviceProvider,
IUserRepository userRepository,
ICurrentContext currentContext)
public class UserStore :
IUserStore<User>,
IUserPasswordStore<User>,
IUserEmailStore<User>,
IUserTwoFactorStore<User>,
IUserSecurityStampStore<User>
{
_serviceProvider = serviceProvider;
_userRepository = userRepository;
_currentContext = currentContext;
}
private readonly IServiceProvider _serviceProvider;
private readonly IUserRepository _userRepository;
private readonly ICurrentContext _currentContext;
public void Dispose() { }
public async Task<IdentityResult> CreateAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
await _userRepository.CreateAsync(user);
return IdentityResult.Success;
}
public async Task<IdentityResult> DeleteAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
await _userRepository.DeleteAsync(user);
return IdentityResult.Success;
}
public async Task<User> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
{
if (_currentContext?.User != null && _currentContext.User.Email == normalizedEmail)
public UserStore(
IServiceProvider serviceProvider,
IUserRepository userRepository,
ICurrentContext currentContext)
{
_serviceProvider = serviceProvider;
_userRepository = userRepository;
_currentContext = currentContext;
}
public void Dispose() { }
public async Task<IdentityResult> CreateAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
await _userRepository.CreateAsync(user);
return IdentityResult.Success;
}
public async Task<IdentityResult> DeleteAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
await _userRepository.DeleteAsync(user);
return IdentityResult.Success;
}
public async Task<User> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
{
if (_currentContext?.User != null && _currentContext.User.Email == normalizedEmail)
{
return _currentContext.User;
}
_currentContext.User = await _userRepository.GetByEmailAsync(normalizedEmail);
return _currentContext.User;
}
_currentContext.User = await _userRepository.GetByEmailAsync(normalizedEmail);
return _currentContext.User;
}
public async Task<User> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
{
if (_currentContext?.User != null &&
string.Equals(_currentContext.User.Id.ToString(), userId, StringComparison.InvariantCultureIgnoreCase))
public async Task<User> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
{
if (_currentContext?.User != null &&
string.Equals(_currentContext.User.Id.ToString(), userId, StringComparison.InvariantCultureIgnoreCase))
{
return _currentContext.User;
}
Guid userIdGuid;
if (!Guid.TryParse(userId, out userIdGuid))
{
return null;
}
_currentContext.User = await _userRepository.GetByIdAsync(userIdGuid);
return _currentContext.User;
}
Guid userIdGuid;
if (!Guid.TryParse(userId, out userIdGuid))
public async Task<User> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
{
return null;
return await FindByEmailAsync(normalizedUserName, cancellationToken);
}
_currentContext.User = await _userRepository.GetByIdAsync(userIdGuid);
return _currentContext.User;
}
public Task<string> GetEmailAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public async Task<User> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
{
return await FindByEmailAsync(normalizedUserName, cancellationToken);
}
public Task<bool> GetEmailConfirmedAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.EmailVerified);
}
public Task<string> GetEmailAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<string> GetNormalizedEmailAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<bool> GetEmailConfirmedAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.EmailVerified);
}
public Task<string> GetNormalizedUserNameAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<string> GetNormalizedEmailAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<string> GetPasswordHashAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.MasterPassword);
}
public Task<string> GetNormalizedUserNameAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<string> GetUserIdAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Id.ToString());
}
public Task<string> GetPasswordHashAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.MasterPassword);
}
public Task<string> GetUserNameAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task<string> GetUserIdAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Id.ToString());
}
public Task<bool> HasPasswordAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(!string.IsNullOrWhiteSpace(user.MasterPassword));
}
public Task<string> GetUserNameAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.Email);
}
public Task SetEmailAsync(User user, string email, CancellationToken cancellationToken = default(CancellationToken))
{
user.Email = email;
return Task.FromResult(0);
}
public Task<bool> HasPasswordAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(!string.IsNullOrWhiteSpace(user.MasterPassword));
}
public Task SetEmailConfirmedAsync(User user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken))
{
user.EmailVerified = confirmed;
return Task.FromResult(0);
}
public Task SetEmailAsync(User user, string email, CancellationToken cancellationToken = default(CancellationToken))
{
user.Email = email;
return Task.FromResult(0);
}
public Task SetNormalizedEmailAsync(User user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
{
user.Email = normalizedEmail;
return Task.FromResult(0);
}
public Task SetEmailConfirmedAsync(User user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken))
{
user.EmailVerified = confirmed;
return Task.FromResult(0);
}
public Task SetNormalizedUserNameAsync(User user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
user.Email = normalizedName;
return Task.FromResult(0);
}
public Task SetNormalizedEmailAsync(User user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
{
user.Email = normalizedEmail;
return Task.FromResult(0);
}
public Task SetPasswordHashAsync(User user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken))
{
user.MasterPassword = passwordHash;
return Task.FromResult(0);
}
public Task SetNormalizedUserNameAsync(User user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
user.Email = normalizedName;
return Task.FromResult(0);
}
public Task SetUserNameAsync(User user, string userName, CancellationToken cancellationToken = default(CancellationToken))
{
user.Email = userName;
return Task.FromResult(0);
}
public Task SetPasswordHashAsync(User user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken))
{
user.MasterPassword = passwordHash;
return Task.FromResult(0);
}
public async Task<IdentityResult> UpdateAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
user.RevisionDate = user.AccountRevisionDate = DateTime.UtcNow;
await _userRepository.ReplaceAsync(user);
return IdentityResult.Success;
}
public Task SetUserNameAsync(User user, string userName, CancellationToken cancellationToken = default(CancellationToken))
{
user.Email = userName;
return Task.FromResult(0);
}
public Task SetTwoFactorEnabledAsync(User user, bool enabled, CancellationToken cancellationToken)
{
// Do nothing...
return Task.FromResult(0);
}
public async Task<IdentityResult> UpdateAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
user.RevisionDate = user.AccountRevisionDate = DateTime.UtcNow;
await _userRepository.ReplaceAsync(user);
return IdentityResult.Success;
}
public async Task<bool> GetTwoFactorEnabledAsync(User user, CancellationToken cancellationToken)
{
return await _serviceProvider.GetRequiredService<IUserService>().TwoFactorIsEnabledAsync(user);
}
public Task SetTwoFactorEnabledAsync(User user, bool enabled, CancellationToken cancellationToken)
{
// Do nothing...
return Task.FromResult(0);
}
public Task SetSecurityStampAsync(User user, string stamp, CancellationToken cancellationToken)
{
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
public async Task<bool> GetTwoFactorEnabledAsync(User user, CancellationToken cancellationToken)
{
return await _serviceProvider.GetRequiredService<IUserService>().TwoFactorIsEnabledAsync(user);
}
public Task SetSecurityStampAsync(User user, string stamp, CancellationToken cancellationToken)
{
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
public Task<string> GetSecurityStampAsync(User user, CancellationToken cancellationToken)
{
return Task.FromResult(user.SecurityStamp);
public Task<string> GetSecurityStampAsync(User user, CancellationToken cancellationToken)
{
return Task.FromResult(user.SecurityStamp);
}
}
}

View File

@@ -10,145 +10,146 @@ using Fido2NetLib.Objects;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Identity;
public class WebAuthnTokenProvider : IUserTwoFactorTokenProvider<User>
namespace Bit.Core.Identity
{
private readonly IServiceProvider _serviceProvider;
private readonly IFido2 _fido2;
private readonly GlobalSettings _globalSettings;
public WebAuthnTokenProvider(IServiceProvider serviceProvider, IFido2 fido2, GlobalSettings globalSettings)
public class WebAuthnTokenProvider : IUserTwoFactorTokenProvider<User>
{
_serviceProvider = serviceProvider;
_fido2 = fido2;
_globalSettings = globalSettings;
}
private readonly IServiceProvider _serviceProvider;
private readonly IFido2 _fido2;
private readonly GlobalSettings _globalSettings;
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
public WebAuthnTokenProvider(IServiceProvider serviceProvider, IFido2 fido2, GlobalSettings globalSettings)
{
return false;
_serviceProvider = serviceProvider;
_fido2 = fido2;
_globalSettings = globalSettings;
}
var webAuthnProvider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
if (!HasProperMetaData(webAuthnProvider))
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
return false;
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
{
return false;
}
var webAuthnProvider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
if (!HasProperMetaData(webAuthnProvider))
{
return false;
}
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.WebAuthn, user);
}
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.WebAuthn, user);
}
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
return null;
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
{
return null;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
var keys = LoadKeys(provider);
var existingCredentials = keys.Select(key => key.Item2.Descriptor).ToList();
if (existingCredentials.Count == 0)
{
return null;
}
var exts = new AuthenticationExtensionsClientInputs()
{
UserVerificationMethod = true,
AppID = CoreHelpers.U2fAppIdUrl(_globalSettings),
};
var options = _fido2.GetAssertionOptions(existingCredentials, UserVerificationRequirement.Discouraged, exts);
// TODO: Remove this when newtonsoft legacy converters are gone
provider.MetaData["login"] = JsonSerializer.Serialize(options);
var providers = user.GetTwoFactorProviders();
providers[TwoFactorProviderType.WebAuthn] = provider;
user.SetTwoFactorProviders(providers);
await userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn, logEvent: false);
return options.ToJson();
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
var keys = LoadKeys(provider);
var existingCredentials = keys.Select(key => key.Item2.Descriptor).ToList();
if (existingCredentials.Count == 0)
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
return null;
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)) || string.IsNullOrWhiteSpace(token))
{
return false;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
var keys = LoadKeys(provider);
if (!provider.MetaData.ContainsKey("login"))
{
return false;
}
var clientResponse = JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(token,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var jsonOptions = provider.MetaData["login"].ToString();
var options = AssertionOptions.FromJson(jsonOptions);
var webAuthCred = keys.Find(k => k.Item2.Descriptor.Id.SequenceEqual(clientResponse.Id));
if (webAuthCred == null)
{
return false;
}
IsUserHandleOwnerOfCredentialIdAsync callback = (args) => Task.FromResult(true);
var res = await _fido2.MakeAssertionAsync(clientResponse, options, webAuthCred.Item2.PublicKey, webAuthCred.Item2.SignatureCounter, callback);
provider.MetaData.Remove("login");
// Update SignatureCounter
webAuthCred.Item2.SignatureCounter = res.Counter;
var providers = user.GetTwoFactorProviders();
providers[TwoFactorProviderType.WebAuthn].MetaData[webAuthCred.Item1] = webAuthCred.Item2;
user.SetTwoFactorProviders(providers);
await userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn, logEvent: false);
return res.Status == "ok";
}
var exts = new AuthenticationExtensionsClientInputs()
private bool HasProperMetaData(TwoFactorProvider provider)
{
UserVerificationMethod = true,
AppID = CoreHelpers.U2fAppIdUrl(_globalSettings),
};
var options = _fido2.GetAssertionOptions(existingCredentials, UserVerificationRequirement.Discouraged, exts);
// TODO: Remove this when newtonsoft legacy converters are gone
provider.MetaData["login"] = JsonSerializer.Serialize(options);
var providers = user.GetTwoFactorProviders();
providers[TwoFactorProviderType.WebAuthn] = provider;
user.SetTwoFactorProviders(providers);
await userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn, logEvent: false);
return options.ToJson();
}
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)) || string.IsNullOrWhiteSpace(token))
{
return false;
return provider?.MetaData?.Any() ?? false;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
var keys = LoadKeys(provider);
if (!provider.MetaData.ContainsKey("login"))
private List<Tuple<string, TwoFactorProvider.WebAuthnData>> LoadKeys(TwoFactorProvider provider)
{
return false;
}
var keys = new List<Tuple<string, TwoFactorProvider.WebAuthnData>>();
if (!HasProperMetaData(provider))
{
return keys;
}
var clientResponse = JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(token,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
// Support up to 5 keys
for (var i = 1; i <= 5; i++)
{
var keyName = $"Key{i}";
if (provider.MetaData.ContainsKey(keyName))
{
var key = new TwoFactorProvider.WebAuthnData((dynamic)provider.MetaData[keyName]);
var jsonOptions = provider.MetaData["login"].ToString();
var options = AssertionOptions.FromJson(jsonOptions);
keys.Add(new Tuple<string, TwoFactorProvider.WebAuthnData>(keyName, key));
}
}
var webAuthCred = keys.Find(k => k.Item2.Descriptor.Id.SequenceEqual(clientResponse.Id));
if (webAuthCred == null)
{
return false;
}
IsUserHandleOwnerOfCredentialIdAsync callback = (args) => Task.FromResult(true);
var res = await _fido2.MakeAssertionAsync(clientResponse, options, webAuthCred.Item2.PublicKey, webAuthCred.Item2.SignatureCounter, callback);
provider.MetaData.Remove("login");
// Update SignatureCounter
webAuthCred.Item2.SignatureCounter = res.Counter;
var providers = user.GetTwoFactorProviders();
providers[TwoFactorProviderType.WebAuthn].MetaData[webAuthCred.Item1] = webAuthCred.Item2;
user.SetTwoFactorProviders(providers);
await userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn, logEvent: false);
return res.Status == "ok";
}
private bool HasProperMetaData(TwoFactorProvider provider)
{
return provider?.MetaData?.Any() ?? false;
}
private List<Tuple<string, TwoFactorProvider.WebAuthnData>> LoadKeys(TwoFactorProvider provider)
{
var keys = new List<Tuple<string, TwoFactorProvider.WebAuthnData>>();
if (!HasProperMetaData(provider))
{
return keys;
}
// Support up to 5 keys
for (var i = 1; i <= 5; i++)
{
var keyName = $"Key{i}";
if (provider.MetaData.ContainsKey(keyName))
{
var key = new TwoFactorProvider.WebAuthnData((dynamic)provider.MetaData[keyName]);
keys.Add(new Tuple<string, TwoFactorProvider.WebAuthnData>(keyName, key));
}
}
return keys;
}
}

View File

@@ -6,70 +6,71 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using YubicoDotNetClient;
namespace Bit.Core.Identity;
public class YubicoOtpTokenProvider : IUserTwoFactorTokenProvider<User>
namespace Bit.Core.Identity
{
private readonly IServiceProvider _serviceProvider;
private readonly GlobalSettings _globalSettings;
public YubicoOtpTokenProvider(
IServiceProvider serviceProvider,
GlobalSettings globalSettings)
public class YubicoOtpTokenProvider : IUserTwoFactorTokenProvider<User>
{
_serviceProvider = serviceProvider;
_globalSettings = globalSettings;
}
private readonly IServiceProvider _serviceProvider;
private readonly GlobalSettings _globalSettings;
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
public YubicoOtpTokenProvider(
IServiceProvider serviceProvider,
GlobalSettings globalSettings)
{
return false;
_serviceProvider = serviceProvider;
_globalSettings = globalSettings;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
if (!provider?.MetaData.Values.Any(v => !string.IsNullOrWhiteSpace((string)v)) ?? true)
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
{
return false;
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
{
return false;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
if (!provider?.MetaData.Values.Any(v => !string.IsNullOrWhiteSpace((string)v)) ?? true)
{
return false;
}
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.YubiKey, user);
}
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.YubiKey, user);
}
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
return Task.FromResult<string>(null);
}
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
{
return false;
return Task.FromResult<string>(null);
}
if (string.IsNullOrWhiteSpace(token) || token.Length < 32 || token.Length > 48)
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
{
return false;
}
var userService = _serviceProvider.GetRequiredService<IUserService>();
if (!(await userService.CanAccessPremium(user)))
{
return false;
}
var id = token.Substring(0, 12);
if (string.IsNullOrWhiteSpace(token) || token.Length < 32 || token.Length > 48)
{
return false;
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
if (!provider.MetaData.ContainsValue(id))
{
return false;
}
var id = token.Substring(0, 12);
var client = new YubicoClient(_globalSettings.Yubico.ClientId, _globalSettings.Yubico.Key);
if (_globalSettings.Yubico.ValidationUrls != null && _globalSettings.Yubico.ValidationUrls.Length > 0)
{
client.SetUrls(_globalSettings.Yubico.ValidationUrls);
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
if (!provider.MetaData.ContainsValue(id))
{
return false;
}
var client = new YubicoClient(_globalSettings.Yubico.ClientId, _globalSettings.Yubico.Key);
if (_globalSettings.Yubico.ValidationUrls != null && _globalSettings.Yubico.ValidationUrls.Length > 0)
{
client.SetUrls(_globalSettings.Yubico.ValidationUrls);
}
var response = await client.VerifyAsync(token);
return response.Status == YubicoResponseStatus.Ok;
}
var response = await client.VerifyAsync(token);
return response.Status == YubicoResponseStatus.Ok;
}
}