diff options
Diffstat (limited to 'plugins')
17 files changed, 226 insertions, 43 deletions
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/PkiLoginEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/PkiLoginEndpoint.cs index bda5898..618a053 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/PkiLoginEndpoint.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/PkiLoginEndpoint.cs @@ -478,7 +478,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints [JsonPropertyName("failed_attempt_timeout_sec")] public double FailedCountTimeoutSec { get; set; } = 300; - public void Validate() + public void OnValidate() { Validator.ValidateAndThrow(this); } diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/Fido/FidoMfaProcessor.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/Fido/FidoMfaProcessor.cs index 63aa46e..70bc070 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/Fido/FidoMfaProcessor.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/Fido/FidoMfaProcessor.cs @@ -114,7 +114,7 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA.Fido { return new FidoDevUpgradeJson { - Base64UrlChallange = VnEncoding.ToBase64UrlSafeString(challenge, false), + Base64UrlChallange = VnEncoding.Base64UrlEncode(challenge, includePadding: false), Timeout = conf.Timeout, diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/MFAConfig.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/MFAConfig.cs index e44006a..7b7a73f 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/MFAConfig.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/MFAConfig.cs @@ -95,6 +95,6 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA public TimeSpan UpgradeValidFor { get; private set; } = TimeSpan.FromSeconds(120); - public void Validate() => _validator.ValidateAndThrow(this); + public void OnValidate() => _validator.ValidateAndThrow(this); } } diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserEnocdedData.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserEnocdedData.cs index 8711fb9..4ccf93a 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserEnocdedData.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserEnocdedData.cs @@ -93,7 +93,7 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA JsonSerializer.Serialize(ms, instance, Statics.SR_OPTIONS); - store[index] = VnEncoding.ToBase64UrlSafeString(ms.AsSpan(), false); + store[index] = VnEncoding.Base64UrlEncode(ms.AsSpan(), false); } } } diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecConfig.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecConfig.cs index 4c80eac..512add2 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecConfig.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecConfig.cs @@ -190,7 +190,7 @@ namespace VNLib.Plugins.Essentials.Accounts.SecurityProvider [JsonPropertyName("strict_user_agent")] public bool StrictUserAgent { get; set; } = true; - void IOnConfigValidation.Validate() + void IOnConfigValidation.OnValidate() { //Validate the current instance _validator.ValidateAndThrow(this); diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/ClientWebAuthManager.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/ClientWebAuthManager.cs index 2c2058d..804e486 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/ClientWebAuthManager.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/ClientWebAuthManager.cs @@ -216,7 +216,7 @@ namespace VNLib.Plugins.Essentials.Accounts.SecurityProvider clientToken = Convert.ToBase64String(outputBuffer[..(int)bytesEncrypted]); //Encode base64 url safe - serverToken = VnEncoding.ToBase64UrlSafeString(secretBuffer, false); + serverToken = VnEncoding.Base64UrlEncode(secretBuffer, includePadding: false); } finally { diff --git a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/ClientClaimManager.cs b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/ClientClaimManager.cs index d078964..033f577 100644 --- a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/ClientClaimManager.cs +++ b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/ClientClaimManager.cs @@ -120,7 +120,7 @@ namespace VNLib.Plugins.Essentials.Auth.Social Cookies.SetCookie(entity, jwt.Compile()); //Encode and store the signing key in the clien't session - entity.Session[SESSION_SIG_KEY_NAME] = VnEncoding.ToBase64UrlSafeString(sigKey, false); + entity.Session[SESSION_SIG_KEY_NAME] = VnEncoding.Base64UrlEncode(sigKey, false); //Clear the signing key MemoryUtil.InitializeBlock(sigKey); diff --git a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/LoginClaim.cs b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/LoginClaim.cs index 30a51fa..5d1b2dc 100644 --- a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/LoginClaim.cs +++ b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/LoginClaim.cs @@ -65,11 +65,11 @@ namespace VNLib.Plugins.Essentials.Auth.Social RandomHash.GetRandomBytes(buffer.Span); //Base32-Encode nonce and save it - Nonce = VnEncoding.ToBase64UrlSafeString(buffer.Span, false); + Nonce = VnEncoding.Base64UrlEncode(buffer.Span, includePadding: false); } finally { - MemoryUtil.InitializeBlock(buffer.Span); + MemoryUtil.InitializeBlock(ref buffer.GetReference(), buffer.IntLength); } } } diff --git a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/LoginUriBuilder.cs b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/LoginUriBuilder.cs index 4ed6ffd..fc94d5a 100644 --- a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/LoginUriBuilder.cs +++ b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/LoginUriBuilder.cs @@ -96,7 +96,7 @@ namespace VNLib.Plugins.Essentials.Auth.Social ForwardOnlyWriter<char> writer = new(charBuffer); //Append the config redirect path - writer.Append(Config.AccessCodeUrl.OriginalString); + writer.Append(Config.AuthorizationUrl.OriginalString); //begin query arguments writer.AppendSmall("&client_id="); writer.Append(Config.ClientID.Value); diff --git a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/OAuthSiteAdapter.cs b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/OAuthSiteAdapter.cs index 37dd7e0..ef90185 100644 --- a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/OAuthSiteAdapter.cs +++ b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/OAuthSiteAdapter.cs @@ -22,6 +22,7 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ +using System; using System.Net; using System.Text; using System.Threading; @@ -49,7 +50,7 @@ namespace VNLib.Plugins.Essentials.Auth.Social { RestClientOptions poolOptions = new() { - MaxTimeout = 5000, + Timeout = TimeSpan.FromSeconds(5), AutomaticDecompression = DecompressionMethods.All, Encoding = Encoding.UTF8, //disable redirects, api should not redirect diff --git a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/OauthClientConfig.cs b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/OauthClientConfig.cs index a3b43ad..7efd4df 100644 --- a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/OauthClientConfig.cs +++ b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/OauthClientConfig.cs @@ -29,6 +29,8 @@ using System.Collections.Generic; using VNLib.Utils.Logging; using VNLib.Utils.Extensions; using VNLib.Plugins.Extensions.Loading; +using VNLib.Plugins.Essentials.Auth.Social.openid; +using VNLib.Plugins.Extensions.Loading.Configuration; namespace VNLib.Plugins.Essentials.Auth.Social { @@ -42,25 +44,25 @@ namespace VNLib.Plugins.Essentials.Auth.Social public OauthClientConfig(PluginBase plugin, IConfigScope config) { - EndpointPath = config["path"].GetString() ?? throw new KeyNotFoundException($"Missing required key 'path' in config {config.ScopeName}"); - - //Set discord account origin - AccountOrigin = config["account_origin"].GetString() ?? throw new KeyNotFoundException($"Missing required key 'account_origin' in config {config.ScopeName}"); - - //Get the auth and token urls - string authUrl = config["authorization_url"].GetString() ?? throw new KeyNotFoundException($"Missing required key 'authorization_url' in config {config.ScopeName}"); - string tokenUrl = config["token_url"].GetString() ?? throw new KeyNotFoundException($"Missing required key 'token_url' in config {config.ScopeName}"); - string userUrl = config["user_data_url"].GetString() ?? throw new KeyNotFoundException($"Missing required key 'user_data_url' in config {config.ScopeName}"); + EndpointPath = config.GetRequiredProperty("path", p => p.GetString()!); + AccountOrigin = config.GetRequiredProperty("account_origin", p => p.GetString()!); + + OpenIdPortalConfig portalConf = config.Deserialze<OpenIdPortalConfig>()!; + + Validate.NotNull(portalConf.AuthorizationEndpoint, $"Missing authorization endpoint for {config.ScopeName}"); + Validate.NotNull(portalConf.TokenEndpoint, $"Missing token endpoint for {config.ScopeName}"); + Validate.NotNull(portalConf.UserDataEndpoint, $"Missing user-data endpoint for {config.ScopeName}"); + //Create the uris - AccessCodeUrl = new(authUrl); - AccessTokenUrl = new(tokenUrl); - UserDataUrl = new(userUrl); + AuthorizationUrl = new(portalConf.AuthorizationEndpoint); + AccessTokenUrl = new(portalConf.TokenEndpoint); + UserDataUrl = new(portalConf.UserDataEndpoint); - AllowForLocalAccounts = config["allow_for_local"].GetBoolean(); - AllowRegistration = config["allow_registration"].GetBoolean(); - NonceByteSize = config["nonce_size"].GetUInt32(); - RandomPasswordSize = config["password_size"].GetInt32(); - InitClaimValidFor = config["claim_valid_for_sec"].GetTimeSpan(TimeParseType.Seconds); + AllowForLocalAccounts = config.GetValueOrDefault("allow_for_local", p => p.GetBoolean(), false); + AllowRegistration = config.GetValueOrDefault("allow_registration", p => p.GetBoolean(), false); + NonceByteSize = config.GetRequiredProperty("nonce_size", p => p.GetUInt32()); + RandomPasswordSize = config.GetRequiredProperty("password_size", p => p.GetInt32()); + InitClaimValidFor = config.GetRequiredProperty("claim_valid_for_sec", p => p.GetTimeSpan(TimeParseType.Seconds)); //Setup async lazy loaders for secrets ClientID = plugin.GetSecretAsync($"{config.ScopeName}_client_id") @@ -101,7 +103,7 @@ namespace VNLib.Plugins.Essentials.Auth.Social /// The URL to redirect the user to the OAuth2 service /// to begin the authentication process /// </summary> - public Uri AccessCodeUrl { get; } + public Uri AuthorizationUrl { get; } /// <summary> /// The remote endoint to exchange codes for access tokens diff --git a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/SocialOauthBase.cs b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/SocialOauthBase.cs index 91bf147..f381fb8 100644 --- a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/SocialOauthBase.cs +++ b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/SocialOauthBase.cs @@ -63,7 +63,11 @@ namespace VNLib.Plugins.Essentials.Auth.Social const string AUTH_GRANT_SESSION_NAME = "auth"; const string SESSION_TOKEN_KEY_NAME = "soa.tkn"; const string CLAIM_COOKIE_NAME = "extern-claim"; - + + private static readonly IValidator<LoginClaim> ClaimValidator = GetClaimValidator(); + private static readonly IValidator<string> NonceValidator = GetNonceValidator(); + private static readonly AccountDataValidator AccountDataValidator = new (); + /// <summary> /// The client configuration struct passed during base class construction @@ -81,19 +85,13 @@ namespace VNLib.Plugins.Essentials.Auth.Social /// <summary> /// The user manager used to create and manage user accounts /// </summary> - protected IUserManager Users { get; } - - private readonly IValidator<LoginClaim> ClaimValidator; - private readonly IValidator<string> NonceValidator; - private readonly IValidator<AccountData> AccountDataValidator; + protected IUserManager Users { get; } + + private readonly ClientClaimManager _claims; protected SocialOauthBase(PluginBase plugin, IConfigScope config) { - ClaimValidator = GetClaimValidator(); - NonceValidator = GetNonceValidator(); - AccountDataValidator = new AccountDataValidator(); - //Get the configuration element for the derrived type Config = plugin.CreateService<OauthClientConfig>(config); diff --git a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/openid/OpenIdPortalConfig.cs b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/openid/OpenIdPortalConfig.cs new file mode 100644 index 0000000..97736ab --- /dev/null +++ b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/openid/OpenIdPortalConfig.cs @@ -0,0 +1,46 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.Auth.Social +* File: OpenIdPortalConfig.cs +* +* OpenIdPortalConfig.cs is part of VNLib.Plugins.Essentials.Auth.Social which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.Auth.Social is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.Auth.Social is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System.Text.Json.Serialization; + +namespace VNLib.Plugins.Essentials.Auth.Social.openid +{ + public sealed class OpenIdPortalConfig + { + [JsonPropertyName("issuer")] + public string IssuerUrl { get; set; } + + [JsonPropertyName("authorization_endpoint")] + public string AuthorizationEndpoint { get; set; } + + [JsonPropertyName("token_endpoint")] + public string TokenEndpoint { get; set; } + + [JsonPropertyName("userinfo_endpoint")] + public string UserDataEndpoint { get; set; } + + [JsonPropertyName("jwks_uri")] + public string KeysEndpoint { get; set; } + } +} diff --git a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/openid/OpenIdProviderValidator.cs b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/openid/OpenIdProviderValidator.cs new file mode 100644 index 0000000..bde0a88 --- /dev/null +++ b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/openid/OpenIdProviderValidator.cs @@ -0,0 +1,70 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.Auth.Social +* File: OauthClientConfig.cs +* +* OauthClientConfig.cs is part of VNLib.Plugins.Essentials.Auth.Social which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.Auth.Social is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.Auth.Social is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; + +using FluentValidation; + +namespace VNLib.Plugins.Essentials.Auth.Social.openid +{ + public sealed class OpenIdProviderValidator : AbstractValidator<OpenIdPortalConfig> + { + public OpenIdProviderValidator(string discoveryUrl) + { + /* + * Discovery url will be compared to make sure the + * host is on the same domain as the issuer + */ + Uri discUrl = new(discoveryUrl); + + RuleFor(i => i.IssuerUrl) + .Matches($"^{discUrl.Scheme}://{discUrl.Host}") + .WithMessage("Issuer must be on the same domain as the discovery url"); + + RuleFor(i => i.AuthorizationEndpoint) + .NotEmpty() + .WithMessage("Authorization endpoint is required") + .Matches($"^{discUrl.Scheme}://{discUrl.Host}") + .WithMessage("Authorization endpoint must be on the same domain as the discovery url"); + + RuleFor(i => i.TokenEndpoint) + .NotEmpty() + .WithMessage("Token endpoint is required") + .Matches($"^{discUrl.Scheme}://{discUrl.Host}") + .WithMessage("Token endpoint must be on the same domain as the discovery url"); + + RuleFor(i => i.UserDataEndpoint) + .NotEmpty() + .WithMessage("User data endpoint is required") + .Matches($"^{discUrl.Scheme}://{discUrl.Host}") + .WithMessage("User data endpoint must be on the same domain as the discovery url"); + + RuleFor(i => i.KeysEndpoint) + .NotEmpty() + .WithMessage("Keys endpoint is required") + .Matches($"^{discUrl.Scheme}://{discUrl.Host}") + .WithMessage("Keys endpoint must be on the same domain as the discovery url"); + } + } +} diff --git a/plugins/VNLib.Plugins.Essentials.Auth.Social/src/openid/OpenIdResolver.cs b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/openid/OpenIdResolver.cs new file mode 100644 index 0000000..652b622 --- /dev/null +++ b/plugins/VNLib.Plugins.Essentials.Auth.Social/src/openid/OpenIdResolver.cs @@ -0,0 +1,67 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.Auth.Social +* File: OauthClientConfig.cs +* +* OauthClientConfig.cs is part of VNLib.Plugins.Essentials.Auth.Social which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.Auth.Social is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.Auth.Social is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System.Threading.Tasks; +using System.Threading; +using VNLib.Net.Rest.Client.Construction; +using RestSharp; + +namespace VNLib.Plugins.Essentials.Auth.Social.openid +{ + /// <summary> + /// Resolves the openid connect configuration from a discovery url + /// </summary> + public sealed class OpenIdResolver + { + private readonly RestSiteAdapterBase _defaultAdapter = RestSiteAdapterBase.CreateSimpleAdapter(); + + /// <summary> + /// Initializes a new instance of the <see cref="OpenIdResolver"/> class + /// </summary> + public OpenIdResolver() + { + _defaultAdapter.DefineSingleEndpoint() + .WithEndpoint<DiscoveryRequest>() + .WithMethod(Method.Get) + .WithUrl(m => m.DiscoUrl) + .WithHeader("Accept", "application/json") + .OnResponse((r, rr) => rr.ThrowIfError()); + } + + /// <summary> + /// Resolves the openid connect configuration from the discovery url + /// </summary> + /// <param name="discoveryUrl">The openid connect discovery url</param> + /// <param name="cancellation">A token to cancel the resolution operation</param> + /// <returns>A task that resolves the openid connect configuration data</returns> + public Task<OpenIdPortalConfig?> ResolveAsync(string discoveryUrl, CancellationToken cancellation) + { + return _defaultAdapter.ExecuteAsync(entity: new DiscoveryRequest(discoveryUrl), cancellation) + .AsJson<OpenIdPortalConfig>(); + } + + sealed record class DiscoveryRequest(string DiscoUrl) + { } + } +} diff --git a/plugins/providers/VNLib.Plugins.Essentials.Auth.Auth0/src/Auth0Portal.cs b/plugins/providers/VNLib.Plugins.Essentials.Auth.Auth0/src/Auth0Portal.cs index a698806..2f91999 100644 --- a/plugins/providers/VNLib.Plugins.Essentials.Auth.Auth0/src/Auth0Portal.cs +++ b/plugins/providers/VNLib.Plugins.Essentials.Auth.Auth0/src/Auth0Portal.cs @@ -30,7 +30,7 @@ using VNLib.Plugins.Essentials.Auth.Auth0.Endpoints; namespace VNLib.Plugins.Essentials.Auth.Auth0 { - + [ServiceExport] [ConfigurationName(ConfigKey)] public sealed class Auth0Portal(PluginBase plugin, IConfigScope config) : IOAuthProvider diff --git a/plugins/providers/VNLib.Plugins.Essentials.Auth.Auth0/src/Endpoints/LoginEndpoint.cs b/plugins/providers/VNLib.Plugins.Essentials.Auth.Auth0/src/Endpoints/LoginEndpoint.cs index 52be461..1937aef 100644 --- a/plugins/providers/VNLib.Plugins.Essentials.Auth.Auth0/src/Endpoints/LoginEndpoint.cs +++ b/plugins/providers/VNLib.Plugins.Essentials.Auth.Auth0/src/Endpoints/LoginEndpoint.cs @@ -27,7 +27,6 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using System.Collections.Generic; using RestSharp; @@ -56,7 +55,7 @@ namespace VNLib.Plugins.Essentials.Auth.Auth0.Endpoints public LoginEndpoint(PluginBase plugin, IConfigScope config) : base(plugin, config) { - string keyUrl = config["key_url"].GetString() ?? throw new KeyNotFoundException("Missing Auth0 'key_url' from config"); + string keyUrl = config.GetRequiredProperty("key_url", p => p.GetString()!); //Define the key endpoint SiteAdapter.DefineSingleEndpoint() @@ -88,7 +87,7 @@ namespace VNLib.Plugins.Essentials.Auth.Auth0.Endpoints //Create a new jwk from each key element in the response ReadOnlyJsonWebKey[] keys = doc.RootElement.GetProperty("keys") .EnumerateArray() - .Select(static k => new ReadOnlyJsonWebKey(k)) + .Select(static k => new ReadOnlyJsonWebKey(in k)) .ToArray(); Log.Debug("Found {count} Auth0 signing keys", keys.Length); |