diff options
Diffstat (limited to 'plugins')
6 files changed, 72 insertions, 58 deletions
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj index bdd9fde..821a308 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj +++ b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj @@ -7,7 +7,7 @@ <GenerateDocumentationFile>False</GenerateDocumentationFile> <Title>VNLib.Plugins.Essentials.Accounts.Registration</Title> <Authors>Vaughn Nugent</Authors> - <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> + <Copyright>Copyright © 2023 Vaughn Nugent</Copyright> <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> <ProduceReferenceAssembly>False</ProduceReferenceAssembly> <SignAssembly>False</SignAssembly> @@ -16,13 +16,13 @@ <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> <SignAssembly>True</SignAssembly> <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> + <AnalysisLevel>latest-all</AnalysisLevel> </PropertyGroup> <!-- Resolve nuget dll files and store them in the output dir --> <PropertyGroup> <!--Enable dynamic loading--> <EnableDynamicLoading>true</EnableDynamicLoading> - <AnalysisLevel>latest-all</AnalysisLevel> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <Deterministic>False</Deterministic> @@ -43,14 +43,8 @@ <ProjectReference Include="..\..\..\..\Extensions\lib\VNLib.Plugins.Extensions.Validation\src\VNLib.Plugins.Extensions.Validation.csproj" /> </ItemGroup> - <ItemGroup> - <None Update="Essentials.EmailRegistration.json"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> - </None> - </ItemGroup> - <Target Name="PostBuild" AfterTargets="PostBuildEvent"> - <Exec Command="start xcopy "$(TargetDir)" "F:\Programming\vnlib\devplugins\$(TargetName)" /E /Y /R" /> + <Exec Command="start xcopy "$(TargetDir)" "..\..\..\..\..\devplugins\$(TargetName)" /E /Y /R" /> </Target> </Project> 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 f7c7909..6f826c0 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 @@ -1,27 +1,31 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> + <Nullable>enable</Nullable> <TargetFramework>net6.0</TargetFramework> <RootNamespace>VNLib.Plugins.Essentials.Accounts</RootNamespace> - <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> - <Authors>Vaughn Nugent</Authors> <AssemblyName>Essentials.Accounts</AssemblyName> - <PackageId>VNLib.Plugins.Essentials.Accounts</PackageId> <Version>1.0.1.5</Version> - <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> <SignAssembly>True</SignAssembly> <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> + <AnalysisLevel>latest-all</AnalysisLevel> </PropertyGroup> + <PropertyGroup> + <Copyright>Copyright © 2023 Vaughn Nugent</Copyright> + <Authors>Vaughn Nugent</Authors> + <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> + <Description> + Essentials.Accounts dynamically loadable IPlugin library for working with user accounts. + </Description> + </PropertyGroup> - <!-- Resolve nuget dll files and store them in the output dir --> <PropertyGroup> <!--Enable dynamic loading--> <EnableDynamicLoading>true</EnableDynamicLoading> - <Nullable>enable</Nullable> - <AnalysisLevel>latest-all</AnalysisLevel> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <Deterministic>False</Deterministic> </PropertyGroup> @@ -48,14 +52,8 @@ <ProjectReference Include="..\..\..\..\Extensions\lib\VNLib.Plugins.Extensions.Validation\src\VNLib.Plugins.Extensions.Validation.csproj" /> </ItemGroup> - <ItemGroup> - <None Update="Essentials.Accounts.json"> - <CopyToOutputDirectory>Always</CopyToOutputDirectory> - </None> - </ItemGroup> - <Target Name="PostBuild" AfterTargets="PostBuildEvent"> - <Exec Command="start xcopy "$(TargetDir)" "F:\Programming\vnlib\devplugins\$(TargetName)" /E /Y /R" /> + <Exec Command="start xcopy "$(TargetDir)" "..\..\..\..\..\devplugins\$(TargetName)" /E /Y /R" /> </Target> </Project> diff --git a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/VNLib.Plugins.Essentials.Content.Routing.csproj b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/VNLib.Plugins.Essentials.Content.Routing.csproj index 7052da0..f7e12d5 100644 --- a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/VNLib.Plugins.Essentials.Content.Routing.csproj +++ b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/VNLib.Plugins.Essentials.Content.Routing.csproj @@ -1,22 +1,22 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> + <Nullable>enable</Nullable> <TargetFramework>net6.0</TargetFramework> <Authors>Vaughn Nugent</Authors> <Version>1.0.1.1</Version> - <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> + <Copyright>Copyright © 2023 Vaughn Nugent</Copyright> <PackageProjectUrl>https://www.vaughnnugent.com</PackageProjectUrl> <AssemblyName>PageRouter</AssemblyName> <SignAssembly>True</SignAssembly> <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> + <AnalysisLevel>latest-all</AnalysisLevel> </PropertyGroup> <!-- Resolve nuget dll files and store them in the output dir --> <PropertyGroup> <!--Enable dynamic loading--> <EnableDynamicLoading>true</EnableDynamicLoading> - <Nullable>enable</Nullable> - <AnalysisLevel>latest-all</AnalysisLevel> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <Deterministic>False</Deterministic> @@ -24,6 +24,7 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <Deterministic>False</Deterministic> </PropertyGroup> + <ItemGroup> <PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2"> <PrivateAssets>all</PrivateAssets> @@ -42,14 +43,8 @@ <ProjectReference Include="..\..\..\..\Extensions\lib\VNLib.Plugins.Extensions.Loading\src\VNLib.Plugins.Extensions.Loading.csproj" /> </ItemGroup> - <ItemGroup> - <None Update="PageRouter.json"> - <CopyToOutputDirectory>Always</CopyToOutputDirectory> - </None> - </ItemGroup> - <Target Name="PostBuild" AfterTargets="PostBuildEvent"> - <Exec Command="start xcopy "$(TargetDir)" "F:\Programming\vnlib\devplugins\$(TargetName)" /E /Y /R" /> + <Exec Command="start xcopy "$(TargetDir)" "..\..\..\..\..\devplugins\$(TargetName)" /E /Y /R" /> </Target> </Project> diff --git a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/Endpoints/Auth0.cs b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/Endpoints/Auth0.cs index c7512b7..586ef96 100644 --- a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/Endpoints/Auth0.cs +++ b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/Endpoints/Auth0.cs @@ -50,7 +50,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints protected override OauthClientConfig Config { get; } - private readonly Task<JsonDocument> Auth0VerificationJwk; + private readonly Task<ReadOnlyJsonWebKey[]> Auth0VerificationJwk; public Auth0(PluginBase plugin, IReadOnlyDictionary<string, JsonElement> config) : base() { @@ -88,7 +88,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints } - private async Task<JsonDocument> GetRsaCertificate(Uri certUri) + private async Task<ReadOnlyJsonWebKey[]> GetRsaCertificate(Uri certUri) { try { @@ -98,13 +98,27 @@ namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints keyRequest.AddHeader("Accept", "application/json"); //rent client from pool - using ClientContract client = ClientPool.Lease(); - - RestResponse response = await client.Resource.ExecuteAsync(keyRequest); + RestResponse response; + + using (ClientContract client = ClientPool.Lease()) + { + response = await client.Resource.ExecuteAsync(keyRequest); + } response.ThrowIfError(); - return JsonDocument.Parse(response.RawBytes); + //Get response as doc + using JsonDocument doc = JsonDocument.Parse(response.RawBytes); + + //Create a new jwk from each key element in the response + ReadOnlyJsonWebKey[] keys = doc.RootElement.GetProperty("keys") + .EnumerateArray() + .Select(static k => new ReadOnlyJsonWebKey(k)) + .ToArray(); + + Log.Debug("Found {count} Auth0 signing keys", keys.Length); + + return keys; } catch (Exception e) { @@ -159,7 +173,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(Auth0VerificationJwk.Result.RootElement.GetProperty("keys").EnumerateArray().First())) + if (!jwt.VerifyFromJwk(Auth0VerificationJwk.Result[0])) { return EmptyLoginData; } diff --git a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/SocialOauthBase.cs b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/SocialOauthBase.cs index 79e3b1b..73c2ab5 100644 --- a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/SocialOauthBase.cs +++ b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/SocialOauthBase.cs @@ -139,6 +139,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth { return false; } + /* * Cross site checking is disabled because we need to allow cross site * for OAuth2 redirect flows @@ -147,12 +148,9 @@ namespace VNLib.Plugins.Essentials.SocialOauth { return false; } + //Make sure the user is not logged in - if(entity.LoginCookieMatches() || entity.TokenMatches()) - { - return false; - } - return true; + return !(entity.LoginCookieMatches() || entity.TokenMatches()); } /// <summary> @@ -219,7 +217,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth /// <returns></returns> protected abstract Task<UserLoginData?> GetLoginDataAsync(IOAuthAccessState clientAccess, CancellationToken cancellation); - class LoginClaim : ICacheable, INonce + sealed class LoginClaim : ICacheable, INonce { [JsonPropertyName("public_key")] public string? PublicKey { get; set; } @@ -282,12 +280,14 @@ namespace VNLib.Plugins.Essentials.SocialOauth entity.Redirect(RedirectType.Temporary, $"{Path}?result=bad_sec"); return VfReturnType.VirtualSkip; } + //Try to get the claim from the state parameter if (ClaimStore.TryGetOrEvictRecord(state, out LoginClaim? claim) < 1) { entity.Redirect(RedirectType.Temporary, $"{Path}?result=expired"); return VfReturnType.VirtualSkip; } + //Lock on the claim to prevent replay lock (claim) { @@ -302,17 +302,21 @@ namespace VNLib.Plugins.Essentials.SocialOauth return VfReturnType.VirtualSkip; } } + //Exchange the OAuth code for a token (application specific) OAuthAccessState? token = await ExchangeCodeForTokenAsync(entity, code, entity.EventCancellation); + //Token may be null if(token == null) { entity.Redirect(RedirectType.Temporary, $"{Path}?result=invalid"); return VfReturnType.VirtualSkip; } + //Store claim info token.PublicKey = claim.PublicKey; token.ClientId = claim.ClientId; + //Generate the new nonce string nonce = token.ComputeNonce((int)Config.NonceByteSize); //Collect expired records @@ -323,6 +327,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth entity.Redirect(RedirectType.Temporary, $"{Path}?result=authorized&nonce={nonce}"); return VfReturnType.VirtualSkip; } + //Check to see if there was an error code set if (entity.QueryArgs.TryGetNonEmptyValue("error", out string? errorCode)) { @@ -330,6 +335,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth entity.Redirect(RedirectType.Temporary, $"{Path}?result=error"); return VfReturnType.VirtualSkip; } + return VfReturnType.ProcessAsFile; } @@ -340,13 +346,16 @@ namespace VNLib.Plugins.Essentials.SocialOauth protected override async ValueTask<VfReturnType> PostAsync(HttpEntity entity) { ValErrWebMessage webm = new(); + //Get the finalization message using JsonDocument? request = await entity.GetJsonFromFileAsync(); + if (webm.Assert(request != null, "Request message is required")) { entity.CloseResponseJson(HttpStatusCode.BadRequest, webm); return VfReturnType.VirtualSkip; } + //Recover the nonce string? base32Nonce = request.RootElement.GetPropString("nonce"); if(webm.Assert(base32Nonce != null, message: "Nonce parameter is required")) @@ -354,12 +363,14 @@ namespace VNLib.Plugins.Essentials.SocialOauth entity.CloseResponseJson(HttpStatusCode.UnprocessableEntity, webm); return VfReturnType.VirtualSkip; } + //Validate nonce if (!NonceValidator.Validate(base32Nonce, webm)) { entity.CloseResponseJson(HttpStatusCode.UnprocessableEntity, webm); return VfReturnType.VirtualSkip; } + //Recover the access token if (AuthorizationStore.TryGetOrEvictRecord(base32Nonce!, out OAuthAccessState? token) < 1) { @@ -367,6 +378,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth entity.CloseResponse(webm); return VfReturnType.VirtualSkip; } + bool valid; //Valid token, now verify the nonce within the locked context lock (token) @@ -375,6 +387,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth //Evict (wipes nonce) AuthorizationStore.EvictRecord(base32Nonce!); } + if (webm.Assert(valid, AUTH_ERROR_MESSAGE)) { entity.CloseResponse(webm); @@ -546,12 +559,16 @@ namespace VNLib.Plugins.Essentials.SocialOauth //Cleanup old records ClaimStore.CollectRecords(); + //Set nonce string base32Nonce = claim.ComputeNonce((int)Config.NonceByteSize); + //build the redirect url webm.Result = BuildUrl(base32Nonce, claim.PublicKey!, entity.IsSecure ? "https" : "http", entity.Server.RequestUri.Authority, entity.Server.Encoding); + //Store the claim ClaimStore.StoreRecord(base32Nonce, claim, Config.LoginNonceLifetime); + webm.Success = true; //Response entity.CloseResponse(webm); @@ -573,11 +590,12 @@ namespace VNLib.Plugins.Essentials.SocialOauth using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(8192, true); //get bin buffer slice Span<byte> binBuffer = buffer.Span[1024..]; + Span<byte> charBuffer = buffer.Span[..1024]; ReadOnlySpan<char> url; { //Get char buffer slice and cast to char - Span<char> charBuf = MemoryMarshal.Cast<byte, char>(buffer.Span[..1024]); + Span<char> charBuf = MemoryMarshal.Cast<byte, char>(charBuffer); //buffer writer for easier syntax ForwardOnlyWriter<char> writer = new(charBuf); //first build the redirect url to re-encode it diff --git a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/VNLib.Plugins.Essentials.SocialOauth.csproj b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/VNLib.Plugins.Essentials.SocialOauth.csproj index 70d6706..95417cb 100644 --- a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/VNLib.Plugins.Essentials.SocialOauth.csproj +++ b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/VNLib.Plugins.Essentials.SocialOauth.csproj @@ -1,15 +1,18 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> + <Nullable>enable</Nullable> <TargetFramework>net6.0</TargetFramework> <Authors>Vaughn Nugent</Authors> <Product>SocialOauth</Product> <Version>1.0.1.5</Version> - <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> + <Copyright>Copyright © 2023 Vaughn Nugent</Copyright> <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> <AssemblyName>SocialOauth</AssemblyName> <SignAssembly>True</SignAssembly> + <GenerateDocumentationFile>False</GenerateDocumentationFile> <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> + <AnalysisLevel>latest-all</AnalysisLevel> </PropertyGroup> <ItemGroup> @@ -27,9 +30,7 @@ <PropertyGroup> <!--Enable dynamic loading--> <EnableDynamicLoading>true</EnableDynamicLoading> - <Nullable>enable</Nullable> - <GenerateDocumentationFile>False</GenerateDocumentationFile> - <AnalysisLevel>latest-all</AnalysisLevel> + <CopyLocalFileLockAssemblies></CopyLocalFileLockAssemblies> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\..\..\..\..\core\lib\Net.Rest.Client\src\VNLib.Net.Rest.Client.csproj" /> @@ -39,14 +40,8 @@ <ProjectReference Include="..\..\..\..\Extensions\lib\VNLib.Plugins.Extensions.Validation\src\VNLib.Plugins.Extensions.Validation.csproj" /> </ItemGroup> - <ItemGroup> - <None Update="SocialOauth.json"> - <CopyToOutputDirectory>Always</CopyToOutputDirectory> - </None> - </ItemGroup> - <Target Name="PostBuild" AfterTargets="PostBuildEvent"> - <Exec Command="start xcopy "$(TargetDir)" "F:\Programming\vnlib\devplugins\$(TargetName)" /E /Y /R" /> + <Exec Command="start xcopy "$(TargetDir)" "..\..\..\..\..\devplugins\$(TargetName)" /E /Y /R" /> </Target> </Project> |