using System; using System.Linq; using System.Threading.Tasks; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Services; using Bit.Core.Settings; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using YubicoDotNetClient; namespace Bit.Core.Identity { public class YubicoOtpTokenProvider : IUserTwoFactorTokenProvider { private readonly IServiceProvider _serviceProvider; private readonly GlobalSettings _globalSettings; public YubicoOtpTokenProvider( IServiceProvider serviceProvider, GlobalSettings globalSettings) { _serviceProvider = serviceProvider; _globalSettings = globalSettings; } public async Task CanGenerateTwoFactorTokenAsync(UserManager manager, User user) { var userService = _serviceProvider.GetRequiredService(); 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); } public Task GenerateAsync(string purpose, UserManager manager, User user) { return Task.FromResult(null); } public async Task ValidateAsync(string purpose, string token, UserManager manager, User user) { var userService = _serviceProvider.GetRequiredService(); if (!(await userService.CanAccessPremium(user))) { return false; } if (string.IsNullOrWhiteSpace(token) || token.Length < 32 || token.Length > 48) { return false; } var id = token.Substring(0, 12); 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; } } }