diff options
Diffstat (limited to 'plugins/VNLib.Plugins.Essentials.Accounts/src')
6 files changed, 49 insertions, 10 deletions
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs index ed79476..f4401a9 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs @@ -110,18 +110,18 @@ namespace VNLib.Plugins.Essentials.Accounts return; } string username = args[uid + 1].Trim(); - string randomUserId = AccountManager.GetRandomUserId(); + string randomUserId = AccountUtil.GetRandomUserId(); //Password as privatestring DANGEROUS to refs using (PrivateString password = (PrivateString)args[pwd + 1].Trim()!) { //Hash the password using PrivateString passHash = Passwords.Hash(password); //Create the user - using IUser user = await Users.CreateUserAsync(randomUserId, username, AccountManager.MINIMUM_LEVEL, passHash); + using IUser user = await Users.CreateUserAsync(randomUserId, username, AccountUtil.MINIMUM_LEVEL, passHash); //Set active flag user.Status = UserStatus.Active; //Set local account - user.SetAccountOrigin(AccountManager.LOCAL_ACCOUNT_ORIGIN); + user.SetAccountOrigin(AccountUtil.LOCAL_ACCOUNT_ORIGIN); await user.ReleaseAsync(); } diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/KeepAliveEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/KeepAliveEndpoint.cs index fe5a65b..0ff0869 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/KeepAliveEndpoint.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/KeepAliveEndpoint.cs @@ -27,14 +27,19 @@ using System.Net; using System.Text.Json; using System.Collections.Generic; +using VNLib.Utils.Extensions; using VNLib.Plugins.Essentials.Endpoints; +using VNLib.Plugins.Essentials.Extensions; using VNLib.Plugins.Extensions.Loading; + namespace VNLib.Plugins.Essentials.Accounts.Endpoints { [ConfigurationName("keepalive_endpoint")] internal sealed class KeepAliveEndpoint : ProtectedWebEndpoint { + readonly TimeSpan tokenRegenTime; + /* * Endpoint does not use a log, so IniPathAndLog is never called * and path verification happens verbosly @@ -43,6 +48,8 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints { string? path = config["path"].GetString(); + tokenRegenTime = config["token_refresh_sec"].GetTimeSpan(TimeParseType.Seconds); + InitPathAndLog(path, pbase.Log); } @@ -56,6 +63,24 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints //Allow post to update user's credentials protected override VfReturnType Post(HttpEntity entity) { + //Get the last token update + DateTimeOffset lastTokenUpdate = entity.Session.LastTokenUpgrade(); + + //See if its expired + if (lastTokenUpdate.Add(tokenRegenTime) < entity.RequestedTimeUtc) + { + //if so updaet token + WebMessage webm = new() + { + Token = entity.RegenerateClientToken(), + Success = true + }; + + //Send the update message to the client + entity.CloseResponse(webm); + return VfReturnType.VirtualSkip; + } + //Return okay entity.CloseResponse(HttpStatusCode.OK); return VfReturnType.VirtualSkip; diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs index 4100620..f973fe8 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs @@ -44,7 +44,7 @@ using VNLib.Plugins.Essentials.Accounts.Validators; using VNLib.Plugins.Extensions.Loading; using VNLib.Plugins.Extensions.Loading.Users; using static VNLib.Plugins.Essentials.Statics; -using static VNLib.Plugins.Essentials.Accounts.AccountManager; +using static VNLib.Plugins.Essentials.Accounts.AccountUtil; namespace VNLib.Plugins.Essentials.Accounts.Endpoints @@ -378,11 +378,14 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints private static string EncryptSecret(string pubKey, byte[] secret) { //Alloc buffer for secret - using IMemoryHandle<byte> buffer = Memory.SafeAlloc<byte>(4096); + using IMemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(4096); + //Try to encrypt the data ERRNO count = TryEncryptClientData(pubKey, secret, buffer.Span); + //Clear secret RandomHash.GetRandomBytes(secret); + //Convert to base64 string return Convert.ToBase64String(buffer.Span[..(int)count]); } @@ -391,11 +394,13 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints { //Recover last counter value TimestampedCounter flc = user.FailedLoginCount(); + if(flc.Count < MaxFailedLogins) { //Period exceeded return false; } + //See if the flc timeout period has expired if (flc.LastModified.Add(FailedCountTimeout) < DateTimeOffset.UtcNow) { @@ -403,6 +408,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints user.FailedLoginCount(0); return false; } + //Count has been exceeded, and has not timed out yet return true; } diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/MFAEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/MFAEndpoint.cs index 6ebb024..df20084 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/MFAEndpoint.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/MFAEndpoint.cs @@ -162,11 +162,13 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints //generate a new secret (passing the buffer which will get copied to an array because the pw bytes can be modified during encryption) byte[] secretBuffer = user.MFAGenreateTOTPSecret(MultiFactor); //Alloc output buffer - UnsafeMemoryHandle<byte> outputBuffer = Memory.UnsafeAlloc<byte>(4096, true); + UnsafeMemoryHandle<byte> outputBuffer = MemoryUtil.UnsafeAlloc<byte>(4096, true); + try { //Encrypt the secret for the client ERRNO count = entity.Session.TryEncryptClientData(secretBuffer, outputBuffer.Span); + if (!count) { webm.Result = "There was an error updating your credentials"; @@ -174,6 +176,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints Log.Warn("TOTP secret encryption failed, for requested user {uid}", entity.Session.UserID); break; } + webm.Result = new TOTPUpdateMessage() { Issuer = MultiFactor.IssuerName, @@ -183,6 +186,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints //Convert the secret to base64 string to send to client Base64EncSecret = Convert.ToBase64String(outputBuffer.Span[..(int)count]) }; + //set success flag webm.Success = true; } diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs index 1ec9953..ee623e2 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs @@ -112,7 +112,7 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA return false; } //Alloc buffer with zero o - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(base32Secret.Length, true); + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(base32Secret.Length, true); ERRNO count = VnEncoding.TryFromBase32Chars(base32Secret, buffer); //Verify the TOTP using the decrypted secret return count && VerifyTOTP(code, buffer.AsSpan(0, count), config); @@ -273,9 +273,11 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA //Verifies a jwt stored signature against the actual signature static bool VerifyStoredSig(ReadOnlySpan<char> base64string, ReadOnlySpan<byte> signature) { - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(base64string.Length, true); + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(base64string.Length, true); + //Recover base64 ERRNO count = VnEncoding.TryFromBase64Chars(base64string, buffer.Span); + //Compare return CryptographicOperations.FixedTimeEquals(signature, buffer.Span[..(int)count]); } @@ -300,8 +302,10 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA //get request body using JsonDocument doc = jwt.GetPayload(); + //Recover issued at time DateTimeOffset iat = DateTimeOffset.FromUnixTimeMilliseconds(doc.RootElement.GetProperty("iat").GetInt64()); + //Verify its not timed out if (iat.Add(config.UpgradeValidFor) < DateTimeOffset.UtcNow) { diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/VNLib.Plugins.Essentials.Accounts.csproj b/plugins/VNLib.Plugins.Essentials.Accounts/src/VNLib.Plugins.Essentials.Accounts.csproj index 98ba1ab..f7c7909 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/VNLib.Plugins.Essentials.Accounts.csproj +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/VNLib.Plugins.Essentials.Accounts.csproj @@ -5,7 +5,7 @@ <RootNamespace>VNLib.Plugins.Essentials.Accounts</RootNamespace> <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> <Authors>Vaughn Nugent</Authors> - <AssemblyName>Accounts</AssemblyName> + <AssemblyName>Essentials.Accounts</AssemblyName> <PackageId>VNLib.Plugins.Essentials.Accounts</PackageId> <Version>1.0.1.5</Version> @@ -49,7 +49,7 @@ </ItemGroup> <ItemGroup> - <None Update="Accounts.json"> + <None Update="Essentials.Accounts.json"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> </ItemGroup> |