aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-01-22 15:02:09 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2023-01-22 15:02:09 -0500
commit2eb099d8b9dfede8dae61252f8b1b300033b0b57 (patch)
treea5d3c2918ef205e180fad7758b797ae10d133919
parentcc9a488a3f01affa1189d104fbe7915b70ba3dd9 (diff)
Project file cleanup, explicit usings
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj12
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/VNLib.Plugins.Essentials.Accounts.csproj26
-rw-r--r--plugins/VNLib.Plugins.Essentials.Content.Routing/src/VNLib.Plugins.Essentials.Content.Routing.csproj15
-rw-r--r--plugins/VNLib.Plugins.Essentials.SocialOauth/src/Endpoints/Auth0.cs28
-rw-r--r--plugins/VNLib.Plugins.Essentials.SocialOauth/src/SocialOauthBase.cs32
-rw-r--r--plugins/VNLib.Plugins.Essentials.SocialOauth/src/VNLib.Plugins.Essentials.SocialOauth.csproj17
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 &quot;$(TargetDir)&quot; &quot;F:\Programming\vnlib\devplugins\$(TargetName)&quot; /E /Y /R" />
+ <Exec Command="start xcopy &quot;$(TargetDir)&quot; &quot;..\..\..\..\..\devplugins\$(TargetName)&quot; /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 &quot;$(TargetDir)&quot; &quot;F:\Programming\vnlib\devplugins\$(TargetName)&quot; /E /Y /R" />
+ <Exec Command="start xcopy &quot;$(TargetDir)&quot; &quot;..\..\..\..\..\devplugins\$(TargetName)&quot; /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 &quot;$(TargetDir)&quot; &quot;F:\Programming\vnlib\devplugins\$(TargetName)&quot; /E /Y /R" />
+ <Exec Command="start xcopy &quot;$(TargetDir)&quot; &quot;..\..\..\..\..\devplugins\$(TargetName)&quot; /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 &quot;$(TargetDir)&quot; &quot;F:\Programming\vnlib\devplugins\$(TargetName)&quot; /E /Y /R" />
+ <Exec Command="start xcopy &quot;$(TargetDir)&quot; &quot;..\..\..\..\..\devplugins\$(TargetName)&quot; /E /Y /R" />
</Target>
</Project>