From 7095ae0ea130200deeac2a45ddc5afbce00b54e3 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 21 Jun 2017 00:04:25 -0400 Subject: [PATCH] Duo WebSDK Token Provider --- src/Api/settings.json | 3 + src/Core/GlobalSettings.cs | 6 ++ src/Core/Identity/DuoWebTokenProvider.cs | 66 +++++++++++++++++++ .../Utilities/ServiceCollectionExtensions.cs | 2 +- src/Identity/settings.json | 7 ++ 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/Core/Identity/DuoWebTokenProvider.cs diff --git a/src/Api/settings.json b/src/Api/settings.json index 4d7a14b90..e768d56c9 100644 --- a/src/Api/settings.json +++ b/src/Api/settings.json @@ -42,6 +42,9 @@ "yubico": { "clientid": "SECRET", "key": "SECRET" + }, + "duo": { + "aKey": "SECRET" } }, "IpRateLimitOptions": { diff --git a/src/Core/GlobalSettings.cs b/src/Core/GlobalSettings.cs index b31422e80..a410e0a9b 100644 --- a/src/Core/GlobalSettings.cs +++ b/src/Core/GlobalSettings.cs @@ -16,6 +16,7 @@ public virtual DocumentDbSettings DocumentDb { get; set; } = new DocumentDbSettings(); public virtual NotificationHubSettings NotificationHub { get; set; } = new NotificationHubSettings(); public virtual YubicoSettings Yubico { get; set; } = new YubicoSettings(); + public virtual DuoSettings Duo { get; set; } = new DuoSettings(); public class SqlServerSettings { @@ -85,5 +86,10 @@ public string ClientId { get; set; } public string Key { get; set; } } + + public class DuoSettings + { + public string AKey { get; set; } + } } } diff --git a/src/Core/Identity/DuoWebTokenProvider.cs b/src/Core/Identity/DuoWebTokenProvider.cs new file mode 100644 index 000000000..f4e1ca579 --- /dev/null +++ b/src/Core/Identity/DuoWebTokenProvider.cs @@ -0,0 +1,66 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Bit.Core.Models.Table; +using Bit.Core.Enums; +using Bit.Core.Utilities.Duo; +using System; +using Bit.Core.Models; + +namespace Bit.Core.Identity +{ + public class DuoWebTokenProvider : IUserTwoFactorTokenProvider + { + private readonly GlobalSettings _globalSettings; + + public DuoWebTokenProvider(GlobalSettings globalSettings) + { + _globalSettings = globalSettings; + } + + public Task CanGenerateTwoFactorTokenAsync(UserManager manager, User user) + { + var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo); + var canGenerate = user.TwoFactorProviderIsEnabled(TwoFactorProviderType.Duo) && HasProperMetaData(provider); + return Task.FromResult(canGenerate); + } + + public Task GenerateAsync(string purpose, UserManager manager, User user) + { + var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo); + if(!HasProperMetaData(provider)) + { + return Task.FromResult(null); + } + + var signatureRequest = DuoWeb.SignRequest(provider.MetaData["IKey"], provider.MetaData["SKey"], + _globalSettings.Duo.AKey, user.Id.ToString()); + return Task.FromResult(signatureRequest); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, User user) + { + var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo); + if(!HasProperMetaData(provider)) + { + return Task.FromResult(false); + } + + var response = DuoWeb.VerifyResponse(provider.MetaData["IKey"], provider.MetaData["SKey"], + _globalSettings.Duo.AKey, token); + + Guid userId; + if(!Guid.TryParse(response, out userId)) + { + return Task.FromResult(false); + } + + return Task.FromResult(userId == user.Id); + } + + private bool HasProperMetaData(TwoFactorProvider provider) + { + return provider?.MetaData != null && provider.MetaData.ContainsKey("IKey") && + provider.MetaData.ContainsKey("SKey") && provider.MetaData.ContainsKey("Host"); + } + } +} diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs index 14f9962d3..2ebe6f5e4 100644 --- a/src/Core/Utilities/ServiceCollectionExtensions.cs +++ b/src/Core/Utilities/ServiceCollectionExtensions.cs @@ -99,7 +99,7 @@ namespace Bit.Core.Utilities .AddRoleStore() .AddTokenProvider(TwoFactorProviderType.Authenticator.ToString()) .AddTokenProvider(TwoFactorProviderType.YubiKey.ToString()) - .AddTokenProvider(TwoFactorProviderType.Duo.ToString()) + .AddTokenProvider(TwoFactorProviderType.Duo.ToString()) .AddTokenProvider>(TokenOptions.DefaultEmailProvider); return identityBuilder; diff --git a/src/Identity/settings.json b/src/Identity/settings.json index 5354976d3..9046e7e48 100644 --- a/src/Identity/settings.json +++ b/src/Identity/settings.json @@ -34,6 +34,13 @@ "notificationHub": { "connectionString": "SECRET", "hubName": "SECRET" + }, + "yubico": { + "clientid": "SECRET", + "key": "SECRET" + }, + "duo": { + "aKey": "SECRET" } } }