using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Infrastructure.Security; public class TwoFactorBackOfficeValidationProvider : TwoFactorValidationProvider where TTwoFactorSetupGenerator : ITwoFactorProvider { public TwoFactorBackOfficeValidationProvider( IDataProtectionProvider dataProtectionProvider, IOptions options, ILogger> logger, ITwoFactorLoginService twoFactorLoginService, TTwoFactorSetupGenerator generator) : base(dataProtectionProvider, options, logger, twoFactorLoginService, generator) { } } public class TwoFactorMemberValidationProvider : TwoFactorValidationProvider where TTwoFactorSetupGenerator : ITwoFactorProvider { public TwoFactorMemberValidationProvider( IDataProtectionProvider dataProtectionProvider, IOptions options, ILogger> logger, ITwoFactorLoginService twoFactorLoginService, TTwoFactorSetupGenerator generator) : base(dataProtectionProvider, options, logger, twoFactorLoginService, generator) { } } public class TwoFactorValidationProvider : DataProtectorTokenProvider where TUmbracoIdentityUser : UmbracoIdentityUser where TTwoFactorSetupGenerator : ITwoFactorProvider { private readonly TTwoFactorSetupGenerator _generator; private readonly ITwoFactorLoginService _twoFactorLoginService; protected TwoFactorValidationProvider( IDataProtectionProvider dataProtectionProvider, IOptions options, ILogger> logger, ITwoFactorLoginService twoFactorLoginService, TTwoFactorSetupGenerator generator) : base(dataProtectionProvider, options, logger) { _twoFactorLoginService = twoFactorLoginService; _generator = generator; } public override Task CanGenerateTwoFactorTokenAsync( UserManager manager, TUmbracoIdentityUser user) => Task.FromResult(_generator is not null); public override async Task ValidateAsync(string purpose, string token, UserManager manager, TUmbracoIdentityUser user) { var secret = await _twoFactorLoginService.GetSecretForUserAndProviderAsync(GetUserKey(user), _generator.ProviderName); if (secret is null) { return false; } var validToken = _generator.ValidateTwoFactorPIN(secret, token); return validToken; } protected Guid GetUserKey(TUmbracoIdentityUser user) { switch (user) { case MemberIdentityUser memberIdentityUser: return memberIdentityUser.Key; case BackOfficeIdentityUser backOfficeIdentityUser: return backOfficeIdentityUser.Key; default: throw new NotSupportedException( "Current we only support MemberIdentityUser and BackOfficeIdentityUser"); } } }