From a5ad943584e91bfbd953dc373a7a313367c7e0ae Mon Sep 17 00:00:00 2001 From: vman Date: Thu, 15 Dec 2022 01:41:41 -0500 Subject: Mfa/login fixes --- .../Endpoints/Auth0.cs | 45 ++++++++++++---------- .../Endpoints/DiscordOauth.cs | 30 +++++++++------ .../Endpoints/GitHubOauth.cs | 37 ++++++++++-------- .../OauthClientConfig.cs | 14 +++---- .../SocialOauthBase.cs | 6 +-- .../VNLib.Plugins.Essentials.SocialOauth.csproj | 2 +- 6 files changed, 72 insertions(+), 62 deletions(-) (limited to 'VNLib.Plugins.Essentials.SocialOauth') diff --git a/VNLib.Plugins.Essentials.SocialOauth/Endpoints/Auth0.cs b/VNLib.Plugins.Essentials.SocialOauth/Endpoints/Auth0.cs index 8518ea0..c7512b7 100644 --- a/VNLib.Plugins.Essentials.SocialOauth/Endpoints/Auth0.cs +++ b/VNLib.Plugins.Essentials.SocialOauth/Endpoints/Auth0.cs @@ -40,46 +40,51 @@ using VNLib.Plugins.Essentials.Accounts; using VNLib.Plugins.Extensions.Loading; using VNLib.Plugins.Extensions.Loading.Users; -#nullable enable - namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints { [ConfigurationName("auth0")] - internal class Auth0 : SocialOauthBase + internal sealed class Auth0 : SocialOauthBase { + protected override OauthClientConfig Config { get; } - private readonly Task RsaCertificate; + private readonly Task Auth0VerificationJwk; - public Auth0(PluginBase plugin, IReadOnlyDictionary config) + public Auth0(PluginBase plugin, IReadOnlyDictionary config) : base() { - //Get id/secret - Task secret = plugin.TryGetSecretAsync("auth0_client_secret"); - Task clientId = plugin.TryGetSecretAsync("auth0_client_id"); + string keyUrl = config["key_url"].GetString() ?? throw new KeyNotFoundException("Missing Auth0 'key_url' from config"); - //Wait sync - Task.WaitAll(secret, clientId); + Uri keyUri = new(keyUrl); + + //Get certificate on background thread + Auth0VerificationJwk = Task.Run(() => GetRsaCertificate(keyUri)); Config = new("auth0", config) { - //get gh client secret and id - ClientID = clientId.Result ?? throw new KeyNotFoundException("Missing Auth0 client id from config or vault"), - ClientSecret = secret.Result ?? throw new KeyNotFoundException("Missing Auth0 client secret from config or vault"), - Passwords = plugin.GetPasswords(), Users = plugin.GetUserManager(), }; - string keyUrl = config["key_url"].GetString() ?? throw new KeyNotFoundException("Missing Auth0 'key_url' from config"); + InitPathAndLog(Config.EndpointPath, plugin.Log); - Uri keyUri = new(keyUrl); + //Load secrets + _ = plugin.DeferTask(async () => + { + //Get id/secret + Task secretTask = plugin.TryGetSecretAsync("auth0_client_secret"); + Task clientIdTask = plugin.TryGetSecretAsync("auth0_client_id"); - //Get certificate on background thread - RsaCertificate = Task.Run(() => GetRsaCertificate(keyUri)); + await Task.WhenAll(secretTask, clientIdTask); - InitPathAndLog(Config.EndpointPath, plugin.Log); + using SecretResult? secret = await secretTask; + using SecretResult? clientId = await clientIdTask; + + Config.ClientID = clientId?.Result.ToString() ?? throw new KeyNotFoundException("Missing Auth0 client id from config or vault"); + Config.ClientSecret = secret?.Result.ToString() ?? throw new KeyNotFoundException("Missing the Auth0 client secret from config or vault"); + + }, 100); } @@ -154,7 +159,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints using JsonWebToken jwt = JsonWebToken.Parse(clientAccess.IdToken); //Verify the token against the first signing key - if (!jwt.VerifyFromJwk(RsaCertificate.Result.RootElement.GetProperty("keys").EnumerateArray().First())) + if (!jwt.VerifyFromJwk(Auth0VerificationJwk.Result.RootElement.GetProperty("keys").EnumerateArray().First())) { return EmptyLoginData; } diff --git a/VNLib.Plugins.Essentials.SocialOauth/Endpoints/DiscordOauth.cs b/VNLib.Plugins.Essentials.SocialOauth/Endpoints/DiscordOauth.cs index 6ee7683..d8b2394 100644 --- a/VNLib.Plugins.Essentials.SocialOauth/Endpoints/DiscordOauth.cs +++ b/VNLib.Plugins.Essentials.SocialOauth/Endpoints/DiscordOauth.cs @@ -39,7 +39,6 @@ using VNLib.Plugins.Essentials.Accounts; using VNLib.Plugins.Extensions.Loading; using VNLib.Plugins.Extensions.Loading.Users; -#nullable enable namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints { @@ -50,26 +49,33 @@ namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints public DiscordOauth(PluginBase plugin, IReadOnlyDictionary config) : base() { - //Get id/secret - Task secret = plugin.TryGetSecretAsync("discord_client_secret"); - Task clientId = plugin.TryGetSecretAsync("discord_client_id"); - - //Wait sync - Task.WaitAll(secret, clientId); - Config = new("discord", config) { - //get gh client secret and id - ClientID = clientId.Result ?? throw new KeyNotFoundException("Missing Discord client id from config or vault"), - ClientSecret = secret.Result ?? throw new KeyNotFoundException("Missing the Discord client secret from config or vault"), - Passwords = plugin.GetPasswords(), Users = plugin.GetUserManager(), }; InitPathAndLog(Config.EndpointPath, plugin.Log); + + //Load secrets + _ = plugin.DeferTask(async () => + { + //Get id/secret + Task clientIdTask = plugin.TryGetSecretAsync("discord_client_id"); + Task secretTask = plugin.TryGetSecretAsync("discord_client_secret"); + + await Task.WhenAll(secretTask, clientIdTask); + + using SecretResult? secret = await secretTask; + using SecretResult? clientId = await clientIdTask; + + Config.ClientID = clientId?.Result.ToString() ?? throw new KeyNotFoundException("Missing Discord client id from config or vault"); + Config.ClientSecret = secret?.Result.ToString() ?? throw new KeyNotFoundException("Missing the Discord client secret from config or vault"); + + }, 100); } + private static string GetUserIdFromPlatform(string userName) { return ManagedHash.ComputeHash($"discord|{userName}", HashAlg.SHA1, HashEncodingMode.Hexadecimal); diff --git a/VNLib.Plugins.Essentials.SocialOauth/Endpoints/GitHubOauth.cs b/VNLib.Plugins.Essentials.SocialOauth/Endpoints/GitHubOauth.cs index 0b4fc0f..676f2bb 100644 --- a/VNLib.Plugins.Essentials.SocialOauth/Endpoints/GitHubOauth.cs +++ b/VNLib.Plugins.Essentials.SocialOauth/Endpoints/GitHubOauth.cs @@ -39,8 +39,6 @@ using VNLib.Plugins.Essentials.Accounts; using VNLib.Plugins.Extensions.Loading; using VNLib.Plugins.Extensions.Loading.Users; -#nullable enable - namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints { [ConfigurationName("github")] @@ -49,32 +47,38 @@ namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints private const string GITHUB_V3_ACCEPT = "application/vnd.github.v3+json"; private readonly string UserEmailUrl; - + protected override OauthClientConfig Config { get; } public GitHubOauth(PluginBase plugin, IReadOnlyDictionary config) : base() { - //Get id/secret - Task secret = plugin.TryGetSecretAsync("github_client_secret"); - Task clientId = plugin.TryGetSecretAsync("github_client_id"); - - //Wait sync - Task.WaitAll(secret, clientId); + + UserEmailUrl = config["user_email_url"].GetString() ?? throw new KeyNotFoundException("Missing required key 'user_email_url' for github configuration"); - Config = new(configName: "github", config) + Config = new("github", config) { - //get gh client secret and id - ClientID = clientId.Result ?? throw new KeyNotFoundException("Missing Github client id from config or vault"), - ClientSecret = secret.Result ?? throw new KeyNotFoundException("Missing Github client secret from config or vault"), - Passwords = plugin.GetPasswords(), Users = plugin.GetUserManager(), }; + InitPathAndLog(Config.EndpointPath, plugin.Log); - UserEmailUrl = config["user_email_url"].GetString() ?? throw new KeyNotFoundException("Missing required key 'user_email_url' for github configuration"); + //Load secrets + _ = plugin.DeferTask(async () => + { + //Get id/secret + Task clientIdTask = plugin.TryGetSecretAsync("github_client_id"); + Task secretTask = plugin.TryGetSecretAsync("github_client_secret"); - InitPathAndLog(Config.EndpointPath, plugin.Log); + await Task.WhenAll(secretTask, clientIdTask); + + using SecretResult? secret = await secretTask; + using SecretResult? clientId = await clientIdTask; + + Config.ClientID = clientId?.Result.ToString() ?? throw new KeyNotFoundException("Missing Github client id from config or vault"); + Config.ClientSecret = secret?.Result.ToString() ?? throw new KeyNotFoundException("Missing the Github client secret from config or vault"); + + }, 100); } protected override void StaticClientPoolInitializer(RestClient client) @@ -211,6 +215,5 @@ namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints return accountData; } - } } \ No newline at end of file diff --git a/VNLib.Plugins.Essentials.SocialOauth/OauthClientConfig.cs b/VNLib.Plugins.Essentials.SocialOauth/OauthClientConfig.cs index 6e30802..9caf705 100644 --- a/VNLib.Plugins.Essentials.SocialOauth/OauthClientConfig.cs +++ b/VNLib.Plugins.Essentials.SocialOauth/OauthClientConfig.cs @@ -29,9 +29,7 @@ using System.Collections.Generic; using VNLib.Utils.Extensions; using VNLib.Plugins.Essentials.Users; using VNLib.Plugins.Essentials.Accounts; -using VNLib.Net.Rest.Client; -#nullable enable namespace VNLib.Plugins.Essentials.SocialOauth { @@ -61,15 +59,13 @@ namespace VNLib.Plugins.Essentials.SocialOauth NonceByteSize = config["nonce_size"].GetUInt32(); RandomPasswordSize = config["password_size"].GetInt32(); } + - - public RestClientPool ClientPool { get; } - - public string ClientID { get; init; } + public string ClientID { get; set; } = string.Empty; - public string ClientSecret { get; init; } + public string ClientSecret { get; set; } = string.Empty; + - /// /// The user-account origin value. Specifies that the user account /// was created outside of the local account system @@ -96,7 +92,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth /// /// The user store to create/get users from /// - public IUserManager Users { get; init; } + public IUserManager Users { get; init; } public PasswordHashing Passwords { get; init; } diff --git a/VNLib.Plugins.Essentials.SocialOauth/SocialOauthBase.cs b/VNLib.Plugins.Essentials.SocialOauth/SocialOauthBase.cs index 5728992..6815bf3 100644 --- a/VNLib.Plugins.Essentials.SocialOauth/SocialOauthBase.cs +++ b/VNLib.Plugins.Essentials.SocialOauth/SocialOauthBase.cs @@ -75,7 +75,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth * Disable cross site checking because the OAuth2 flow requires * cross site when redirecting the client back */ - CrossSiteDenied = false + DisableCrossSiteDenied = true }; /// @@ -283,7 +283,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth return VfReturnType.VirtualSkip; } //Try to get the claim from the state parameter - if (ClaimStore.TryGetOrEvictRecord(state, out LoginClaim claim) < 1) + if (ClaimStore.TryGetOrEvictRecord(state, out LoginClaim? claim) < 1) { entity.Redirect(RedirectType.Temporary, $"{Path}?result=expired"); return VfReturnType.VirtualSkip; @@ -361,7 +361,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth return VfReturnType.VirtualSkip; } //Recover the access token - if (AuthorizationStore.TryGetOrEvictRecord(base32Nonce!, out OAuthAccessState token) < 1) + if (AuthorizationStore.TryGetOrEvictRecord(base32Nonce!, out OAuthAccessState? token) < 1) { webm.Result = AUTH_ERROR_MESSAGE; entity.CloseResponse(webm); diff --git a/VNLib.Plugins.Essentials.SocialOauth/VNLib.Plugins.Essentials.SocialOauth.csproj b/VNLib.Plugins.Essentials.SocialOauth/VNLib.Plugins.Essentials.SocialOauth.csproj index bbe9185..56e3068 100644 --- a/VNLib.Plugins.Essentials.SocialOauth/VNLib.Plugins.Essentials.SocialOauth.csproj +++ b/VNLib.Plugins.Essentials.SocialOauth/VNLib.Plugins.Essentials.SocialOauth.csproj @@ -32,7 +32,7 @@ latest-all - + -- cgit