using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Bit.Core.Models.Table; using Bit.Core.Enums; using Bit.Core.Utilities.Duo; using System.Collections.Generic; using System.Net.Http; namespace Bit.Core.Identity { public class DuoTokenProvider : IUserTwoFactorTokenProvider { public Task CanGenerateTwoFactorTokenAsync(UserManager manager, User user) { if(!user.Premium) { return Task.FromResult(false); } var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo); var canGenerate = user.TwoFactorProviderIsEnabled(TwoFactorProviderType.Duo) && !string.IsNullOrWhiteSpace((string)provider?.MetaData["UserId"]); return Task.FromResult(canGenerate); } /// Ex: "auto", "push", "passcode:123456", "sms", "phone" public async Task GenerateAsync(string purpose, UserManager manager, User user) { if(!user.Premium) { return Task.FromResult(null); } var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo); var duoClient = new DuoApi((string)provider.MetaData["IKey"], (string)provider.MetaData["SKey"], (string)provider.MetaData["Host"]); var parts = purpose.Split(':'); var parameters = new Dictionary { ["async"] = "1", ["user_id"] = (string)provider.MetaData["UserId"], ["factor"] = parts[0] }; if(parameters["factor"] == "passcode" && parts.Length > 1) { parameters["passcode"] = parts[1]; } else { parameters["device"] = "auto"; } try { var response = await duoClient.JSONApiCallAsync>(HttpMethod.Post, "/auth/v2/auth", parameters); if(response.ContainsKey("txid")) { var txId = response["txid"] as string; return txId; } } catch(DuoException) { } return null; } public async Task ValidateAsync(string purpose, string token, UserManager manager, User user) { if(!user.Premium) { return false; } var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo); var duoClient = new DuoApi((string)provider.MetaData["IKey"], (string)provider.MetaData["SKey"], (string)provider.MetaData["Host"]); var parameters = new Dictionary { ["txid"] = token }; try { var response = await duoClient.JSONApiCallAsync>(HttpMethod.Get, "/auth/v2/auth_status", parameters); var result = response["result"] as string; return string.Equals(result, "allow"); } catch(DuoException) { // TODO: We might want to return true in some cases? What if Duo is down? } return false; } } }