diff options
15 files changed, 119 insertions, 123 deletions
diff --git a/VNLib.Plugins.Essentials.Accounts.Admin/VNLib.Plugins.Essentials.Accounts.Admin.csproj b/VNLib.Plugins.Essentials.Accounts.Admin/VNLib.Plugins.Essentials.Accounts.Admin.csproj index 00afb08..53cc189 100644 --- a/VNLib.Plugins.Essentials.Accounts.Admin/VNLib.Plugins.Essentials.Accounts.Admin.csproj +++ b/VNLib.Plugins.Essentials.Accounts.Admin/VNLib.Plugins.Essentials.Accounts.Admin.csproj @@ -27,7 +27,7 @@ </ItemGroup> <ItemGroup> - <ProjectReference Include="..\..\..\VNLib\Essentials\VNLib.Plugins.Essentials.csproj" /> + <ProjectReference Include="..\..\..\VNLib\Essentials\src\VNLib.Plugins.Essentials.csproj" /> <ProjectReference Include="..\..\Extensions\VNLib.Plugins.Extensions.Data\VNLib.Plugins.Extensions.Data.csproj" /> <ProjectReference Include="..\..\Extensions\VNLib.Plugins.Extensions.Loading.Sql\VNLib.Plugins.Extensions.Loading.Sql.csproj" /> <ProjectReference Include="..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" /> diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/EmailSystemConfig.cs b/VNLib.Plugins.Essentials.Accounts.Registration/src/EmailSystemConfig.cs index 56c7f37..238ae37 100644 --- a/VNLib.Plugins.Essentials.Accounts.Registration/src/EmailSystemConfig.cs +++ b/VNLib.Plugins.Essentials.Accounts.Registration/src/EmailSystemConfig.cs @@ -65,17 +65,18 @@ namespace VNLib.Plugins.Essentials.Accounts.Registration //Load oauth secrets from vault - Task<string?> oauth2ClientID = pbase.TryGetSecretAsync("oauth2_client_id"); - Task<string?> oauth2Password = pbase.TryGetSecretAsync("oauth2_client_secret"); + Task<SecretResult?> oauth2ClientID = pbase.TryGetSecretAsync("oauth2_client_id"); + Task<SecretResult?> oauth2Password = pbase.TryGetSecretAsync("oauth2_client_secret"); //Lazy cred loaded, tasks should be loaded before this method will ever get called Credential lazyCredentialGet() { //Load the results - string cliendId = oauth2ClientID.Result ?? throw new KeyNotFoundException("Missing required oauth2 client id"); - string password = oauth2Password.Result ?? throw new KeyNotFoundException("Missing required oauth2 client secret"); + SecretResult cliendId = oauth2ClientID.Result ?? throw new KeyNotFoundException("Missing required oauth2 client id"); + SecretResult password = oauth2Password.Result ?? throw new KeyNotFoundException("Missing required oauth2 client secret"); - return Credential.Create(cliendId, password); + //Creat credential + return Credential.Create(cliendId.Result, password.Result); } @@ -113,6 +114,8 @@ namespace VNLib.Plugins.Essentials.Accounts.Registration { authenticator.Dispose(); RestClientPool.Dispose(); + oauth2ClientID.Dispose(); + oauth2Password.Dispose(); } //register password cleanup diff --git a/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs b/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs index 1e983cb..b56cb3c 100644 --- a/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs +++ b/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs @@ -100,8 +100,9 @@ namespace VNLib.Plugins.Essentials.Accounts.Registration.Endpoints //Begin the async op to get the signature key from the vault RegSignatureKey = plugin.TryGetSecretAsync("reg_sig_key").ContinueWith((ts) => { - _ = ts.Result ?? throw new KeyNotFoundException("Missing required key 'reg_sig_key' in 'registration' configuration"); - return Convert.FromBase64String(ts.Result); + using SecretResult? sr = ts.Result ?? throw new KeyNotFoundException("Missing required key 'reg_sig_key' in 'registration' configuration"); + return ts.Result.GetFromBase64(); + }, TaskScheduler.Default); //Register timeout for cleanup diff --git a/VNLib.Plugins.Essentials.Accounts/Endpoints/LoginEndpoint.cs b/VNLib.Plugins.Essentials.Accounts/Endpoints/LoginEndpoint.cs index a4254de..4100620 100644 --- a/VNLib.Plugins.Essentials.Accounts/Endpoints/LoginEndpoint.cs +++ b/VNLib.Plugins.Essentials.Accounts/Endpoints/LoginEndpoint.cs @@ -68,9 +68,6 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints private readonly uint MaxFailedLogins; private readonly TimeSpan FailedCountTimeout; - ///<inheritdoc/> - protected override ProtectionSettings EndpointProtectionSettings { get; } = new(); - public LoginEndpoint(PluginBase pbase, IReadOnlyDictionary<string, JsonElement> config) { string? path = config["path"].GetString(); @@ -298,15 +295,19 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints return VfReturnType.VirtualSkip; } - //Wipe session signature - entity.Session.MfaUpgradeSignature(null); + bool locked = UserLoginLocked(user); //Make sure the account has not been locked out - if (!webm.Assert(!UserLoginLocked(user), LOCKED_ACCOUNT_MESSAGE)) + if (!webm.Assert(locked == false, LOCKED_ACCOUNT_MESSAGE)) { //process mfa login LoginMfa(entity, user, request, upgrade, webm); } + else + { + //Locked, so clear stored signature + entity.Session.MfaUpgradeSignature(null); + } //Update user on clean process await user.ReleaseAsync(); @@ -338,7 +339,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints //Valid, complete } break; - case MFAType.GPG: + case MFAType.PGP: { } break; default: @@ -347,6 +348,10 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints } return; } + + //Wipe session signature + entity.Session.MfaUpgradeSignature(null); + //build login message from upgrade LoginMessage loginMessage = new() { diff --git a/VNLib.Plugins.Essentials.Accounts/Endpoints/LogoutEndpoint.cs b/VNLib.Plugins.Essentials.Accounts/Endpoints/LogoutEndpoint.cs index f5d12ec..cc36609 100644 --- a/VNLib.Plugins.Essentials.Accounts/Endpoints/LogoutEndpoint.cs +++ b/VNLib.Plugins.Essentials.Accounts/Endpoints/LogoutEndpoint.cs @@ -34,12 +34,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints { [ConfigurationName("logout_endpoint")] internal class LogoutEndpoint : ProtectedWebEndpoint - { - //Use default ep protection (most strict) - - ///<inheritdoc/> - protected override ProtectionSettings EndpointProtectionSettings { get; } = new(); - + { public LogoutEndpoint(PluginBase pbase, IReadOnlyDictionary<string, JsonElement> config) { diff --git a/VNLib.Plugins.Essentials.Accounts/MFA/MFAConfig.cs b/VNLib.Plugins.Essentials.Accounts/MFA/MFAConfig.cs index f04693e..03d5a20 100644 --- a/VNLib.Plugins.Essentials.Accounts/MFA/MFAConfig.cs +++ b/VNLib.Plugins.Essentials.Accounts/MFA/MFAConfig.cs @@ -29,12 +29,13 @@ using System.Collections.Generic; using VNLib.Hashing; using VNLib.Utils.Extensions; +using VNLib.Hashing.IdentityUtility; namespace VNLib.Plugins.Essentials.Accounts.MFA { internal class MFAConfig { - public byte[]? MFASecret { get; set; } + public ReadOnlyJsonWebKey? MFASecret { get; set; } public bool TOTPEnabled { get; } public string? IssuerName { get; } diff --git a/VNLib.Plugins.Essentials.Accounts/MFA/MFAType.cs b/VNLib.Plugins.Essentials.Accounts/MFA/MFAType.cs index 3ace3e6..208eea3 100644 --- a/VNLib.Plugins.Essentials.Accounts/MFA/MFAType.cs +++ b/VNLib.Plugins.Essentials.Accounts/MFA/MFAType.cs @@ -26,6 +26,6 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA { public enum MFAType { - TOTP, FIDO, GPG + TOTP, FIDO, PGP } } diff --git a/VNLib.Plugins.Essentials.Accounts/MFA/UserMFAExtensions.cs b/VNLib.Plugins.Essentials.Accounts/MFA/UserMFAExtensions.cs index 6675a31..4fa76ef 100644 --- a/VNLib.Plugins.Essentials.Accounts/MFA/UserMFAExtensions.cs +++ b/VNLib.Plugins.Essentials.Accounts/MFA/UserMFAExtensions.cs @@ -34,7 +34,6 @@ using System.Runtime.CompilerServices; using VNLib.Hashing; using VNLib.Utils; using VNLib.Utils.Memory; -using VNLib.Utils.Logging; using VNLib.Utils.Extensions; using VNLib.Hashing.IdentityUtility; using VNLib.Plugins.Essentials.Users; @@ -122,16 +121,6 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA private static bool VerifyTOTP(uint totpCode, ReadOnlySpan<byte> userSecret, MFAConfig config) { - //Calc hash size for allocating bufffer - int hashSize = config.TOTPAlg switch - { - HashAlg.MD5 => (160 / 8), - HashAlg.SHA1 => (160 / 8), - HashAlg.SHA512 => (512 / 8), - HashAlg.SHA384 => (384 / 8), - HashAlg.SHA256 => (256 / 8), - _ => throw new ArgumentException("Invalid hash algorithm"), - }; //A basic attempt at a constant time TOTP verification, run the calculation a fixed number of times, regardless of the resutls bool codeMatches = false; @@ -140,7 +129,7 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA //Start the current window with the minimum window int currenStep = -config.TOTPTimeWindowSteps; Span<byte> stepBuffer = stackalloc byte[sizeof(long)]; - Span<byte> hashBuffer = stackalloc byte[hashSize]; + Span<byte> hashBuffer = stackalloc byte[(int)config.TOTPAlg]; //Run the loop at least once to allow a 0 step tight window do { @@ -159,7 +148,7 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA //try to compute the hash of the time step if (result < 1) { - throw new OutOfMemoryException("Failed to compute TOTP time step hash because the buffer was too small"); + throw new InternalBufferTooSmallException("Failed to compute TOTP time step hash because the buffer was too small"); } //Hash bytes ReadOnlySpan<byte> hash = hashBuffer[..(int)result]; @@ -230,9 +219,10 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA //Recover secret from config and dangerous 'lazy load' _ = pbase.DeferTask(async () => { - string? secret = await pbase.TryGetSecretAsync("mfa_secret"); - mfa.MFASecret = secret != null ? Convert.FromBase64String(secret) : null; - }); + using SecretResult? secret = await pbase.TryGetSecretAsync("mfa_secret"); + mfa.MFASecret = secret != null ? secret.GetJsonWebKey() : null; + + },50); return mfa; } @@ -276,12 +266,6 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA #endregion #region webauthn - - private static readonly IReadOnlyDictionary<string, string> JWTHeader = new Dictionary<string, string> - { - { "alg", "HS384" }, - { "typ" , "JWT"} - }; #endregion @@ -294,10 +278,10 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA /// <param name="upgrade">The recovered upgrade</param> /// <param name="base64sessionSig">The stored base64 encoded signature from the session that requested an upgrade</param> /// <returns>True if the upgrade was verified, not expired, and was recovered from the signed message, false otherwise</returns> - public static bool RecoverUpgrade(this MFAConfig config, string upgradeJwtString, string base64sessionSig, [NotNullWhen(true)] out MFAUpgrade? upgrade) + public static bool RecoverUpgrade(this MFAConfig config, ReadOnlySpan<char> upgradeJwtString, ReadOnlySpan<char> base64sessionSig, [NotNullWhen(true)] out MFAUpgrade? upgrade) { //Verifies a jwt stored signature against the actual signature - static bool VerifyStoredSig(string base64string, ReadOnlySpan<byte> signature) + static bool VerifyStoredSig(ReadOnlySpan<char> base64string, ReadOnlySpan<byte> signature) { using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(base64string.Length, true); //Recover base64 @@ -310,15 +294,13 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA _ = config.MFASecret ?? throw new InvalidOperationException("MFA config is missing required upgrade signing key"); upgrade = null; + //Parse jwt using JsonWebToken jwt = JsonWebToken.Parse(upgradeJwtString); - //Verify the upgrade jwt - using (HMACSHA384 hmac = new(config.MFASecret)) + + if (!jwt.VerifyFromJwk(config.MFASecret)) { - if (!jwt.Verify(hmac)) - { - return false; - } + return false; } if(!VerifyStoredSig(base64sessionSig, jwt.SignatureData)) @@ -382,13 +364,13 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA return null; } - private static Tuple<string, string> GetUpgradeMessage(MFAUpgrade upgrade, byte[] secret, TimeSpan expires) + private static Tuple<string, string> GetUpgradeMessage(MFAUpgrade upgrade, ReadOnlyJsonWebKey secret, TimeSpan expires) { //Add some random entropy to the upgrade message, to help prevent forgery string entropy = RandomHash.GetRandomBase32(16); //Init jwt using JsonWebToken upgradeJwt = new(); - upgradeJwt.WriteHeader(JWTHeader); + upgradeJwt.WriteHeader(secret.JwtHeader); //Write claims upgradeJwt.InitPayloadClaim() .AddClaim("iat", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()) @@ -397,11 +379,10 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA .AddClaim("expires", expires.TotalSeconds) .AddClaim("a", entropy) .CommitClaims(); - //Sign - using (HMACSHA384 hmc = new(secret)) - { - upgradeJwt.Sign(hmc); - } + + //Sign with jwk + upgradeJwt.SignFromJwk(secret); + //compile and return jwt upgrade return new(upgradeJwt.Compile(), Convert.ToBase64String(upgradeJwt.SignatureData)); } diff --git a/VNLib.Plugins.Essentials.Accounts/VNLib.Plugins.Essentials.Accounts.csproj b/VNLib.Plugins.Essentials.Accounts/VNLib.Plugins.Essentials.Accounts.csproj index 719f8df..15ac5e5 100644 --- a/VNLib.Plugins.Essentials.Accounts/VNLib.Plugins.Essentials.Accounts.csproj +++ b/VNLib.Plugins.Essentials.Accounts/VNLib.Plugins.Essentials.Accounts.csproj @@ -40,7 +40,7 @@ </ItemGroup> <ItemGroup> - <ProjectReference Include="..\..\..\VNLib\Essentials\VNLib.Plugins.Essentials.csproj" /> + <ProjectReference Include="..\..\..\VNLib\Essentials\src\VNLib.Plugins.Essentials.csproj" /> <ProjectReference Include="..\..\Extensions\VNLib.Plugins.Extensions.Loading.Sql\VNLib.Plugins.Extensions.Loading.Sql.csproj" /> <ProjectReference Include="..\..\Extensions\VNLib.Plugins.Extensions.Validation\VNLib.Plugins.Extensions.Validation.csproj" /> <ProjectReference Include="..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" /> 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<JsonDocument> RsaCertificate; + private readonly Task<JsonDocument> Auth0VerificationJwk; - public Auth0(PluginBase plugin, IReadOnlyDictionary<string, JsonElement> config) + public Auth0(PluginBase plugin, IReadOnlyDictionary<string, JsonElement> config) : base() { - //Get id/secret - Task<string?> secret = plugin.TryGetSecretAsync("auth0_client_secret"); - Task<string?> 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<SecretResult?> secretTask = plugin.TryGetSecretAsync("auth0_client_secret"); + Task<SecretResult?> 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<string, JsonElement> config) : base() { - //Get id/secret - Task<string?> secret = plugin.TryGetSecretAsync("discord_client_secret"); - Task<string?> 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<SecretResult?> clientIdTask = plugin.TryGetSecretAsync("discord_client_id"); + Task<SecretResult?> 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<string, JsonElement> config) : base() { - //Get id/secret - Task<string?> secret = plugin.TryGetSecretAsync("github_client_secret"); - Task<string?> 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<SecretResult?> clientIdTask = plugin.TryGetSecretAsync("github_client_id"); + Task<SecretResult?> 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; + - /// <summary> /// 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 /// <summary> /// The user store to create/get users from /// </summary> - 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 }; /// <summary> @@ -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 @@ <AnalysisLevel>latest-all</AnalysisLevel> </PropertyGroup> <ItemGroup> - <ProjectReference Include="..\..\..\VNLib\Essentials\VNLib.Plugins.Essentials.csproj" /> + <ProjectReference Include="..\..\..\VNLib\Essentials\src\VNLib.Plugins.Essentials.csproj" /> <ProjectReference Include="..\..\Extensions\VNLib.Plugins.Extensions.Loading\VNLib.Plugins.Extensions.Loading.csproj" /> <ProjectReference Include="..\..\Extensions\VNLib.Plugins.Extensions.Validation\VNLib.Plugins.Extensions.Validation.csproj" /> <ProjectReference Include="..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" /> |