From 2eb099d8b9dfede8dae61252f8b1b300033b0b57 Mon Sep 17 00:00:00 2001 From: vnugent Date: Sun, 22 Jan 2023 15:02:09 -0500 Subject: Project file cleanup, explicit usings --- .../src/Endpoints/Auth0.cs | 28 ++++++++++++++----- .../src/SocialOauthBase.cs | 32 +++++++++++++++++----- .../VNLib.Plugins.Essentials.SocialOauth.csproj | 17 ++++-------- 3 files changed, 52 insertions(+), 25 deletions(-) (limited to 'plugins/VNLib.Plugins.Essentials.SocialOauth') 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 Auth0VerificationJwk; + private readonly Task Auth0VerificationJwk; public Auth0(PluginBase plugin, IReadOnlyDictionary config) : base() { @@ -88,7 +88,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth.Endpoints } - private async Task GetRsaCertificate(Uri certUri) + private async Task 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()); } /// @@ -219,7 +217,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth /// protected abstract Task 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 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 buffer = MemoryUtil.UnsafeAlloc(8192, true); //get bin buffer slice Span binBuffer = buffer.Span[1024..]; + Span charBuffer = buffer.Span[..1024]; ReadOnlySpan url; { //Get char buffer slice and cast to char - Span charBuf = MemoryMarshal.Cast(buffer.Span[..1024]); + Span charBuf = MemoryMarshal.Cast(charBuffer); //buffer writer for easier syntax ForwardOnlyWriter 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 @@ + enable net6.0 Vaughn Nugent SocialOauth 1.0.1.5 - Copyright © 2022 Vaughn Nugent + Copyright © 2023 Vaughn Nugent https://www.vaughnnugent.com/resources SocialOauth True + False \\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk + latest-all @@ -27,9 +30,7 @@ true - enable - False - latest-all + @@ -39,14 +40,8 @@ - - - Always - - - - + -- cgit