< Summary

Information
Class: Elsa.ExternalAuthentication.Validation.ExternalAuthenticationOptionsValidator
Assembly: Elsa.ExternalAuthentication
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Validation/ExternalAuthenticationOptionsValidator.cs
Line coverage
81%
Covered lines: 181
Uncovered lines: 42
Coverable lines: 223
Total lines: 400
Line coverage: 81.1%
Branch coverage
70%
Covered branches: 166
Total branches: 234
Branch coverage: 70.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Validate(...)50%101095.23%
ValidatePermissionGrantBoundary(...)83.33%6687.5%
ValidatePermissionPatterns(...)87.5%88100%
ValidateExternalCallbackBaseUri(...)83.33%66100%
ValidateHandleHashing(...)87.5%8888.23%
ValidateRateLimits(...)100%88100%
ValidateProviderEgress(...)81.57%393890%
ValidateExtensionTypes(...)83.33%7675%
ValidateAllowedTypes(...)80%111080%
ValidateClients(...)67.64%453478.57%
ValidateUris(...)83.33%6687.5%
ValidateOrigins(...)92.85%141492.85%
ValidateReturnPathPrefixes(...)75%9872.72%
IsExactClientUri(...)57.14%171475%
ValidateConfigurationConnections(...)73.52%413482.14%
ValidatePolicy(...)10%441030%
ValidateGrantSources(...)28.57%891427.27%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Validation/ExternalAuthenticationOptionsValidator.cs

#LineLine coverage
 1using Elsa.Authorization;
 2using Elsa.ExternalAuthentication.Models;
 3using Elsa.ExternalAuthentication.Options;
 4using Elsa.ExternalAuthentication.Policies;
 5using Microsoft.Extensions.Logging;
 6using Microsoft.Extensions.Options;
 7
 8namespace Elsa.ExternalAuthentication.Validation;
 9
 10/// <summary>
 11/// Validates the deployment-owned External Authentication configuration before it is used by the broker.
 12/// </summary>
 3413public sealed class ExternalAuthenticationOptionsValidator(
 3414    IOptions<ExternalAuthenticationExtensionOptions> extensionOptions,
 3415    ILogger<ExternalAuthenticationOptionsValidator> logger) : IValidateOptions<ExternalAuthenticationOptions>
 16{
 17    public ValidateOptionsResult Validate(string? name, ExternalAuthenticationOptions options)
 18    {
 3319        var failures = new List<string>();
 3320        var registrations = extensionOptions.Value.Registrations;
 28421        var installedAdapterTypes = ValidateExtensionTypes(registrations.Where(x => x.Kind == ExternalAuthenticationExte
 31322        var installedPolicyTypes = ValidateExtensionTypes(registrations.Where(x => x.Kind == ExternalAuthenticationExten
 37023        var installedGrantSourceTypes = ValidateExtensionTypes(registrations.Where(x => x.Kind == ExternalAuthentication
 25024        var installedMatcherTypes = ValidateExtensionTypes(registrations.Where(x => x.Kind == ExternalAuthenticationExte
 25
 3326        ValidateAllowedTypes(options.AllowedAdapterTypes, installedAdapterTypes, "adapter", failures);
 3327        ValidateAllowedTypes(options.AllowedUnlinkedIdentityPolicyTypes, installedPolicyTypes, "unlinked identity policy
 3328        ValidateAllowedTypes(options.AllowedPermissionGrantSourceTypes, installedGrantSourceTypes, "permission grant sou
 3329        ValidateAllowedTypes(options.AllowedExternalUserMatcherTypes, installedMatcherTypes, "external user matcher", fa
 3330        if (string.Equals(options.UnlinkedIdentityPolicy.DefaultType, MatchExternalUserUnlinkedIdentityPolicy.PolicyType
 3331            (installedMatcherTypes.Count == 0 || options.AllowedExternalUserMatcherTypes.Count > 0 && !installedMatcherT
 032            failures.Add("The default unlinked identity policy 'match-user' requires at least one installed and allowed 
 3333        ValidateExternalCallbackBaseUri(options.Redirects, failures);
 3334        ValidateClients(options, failures);
 3335        ValidateConfigurationConnections(options, installedAdapterTypes, installedPolicyTypes, installedGrantSourceTypes
 3336        ValidateRateLimits(options.RateLimits, failures);
 3337        ValidateProviderEgress(options.ProviderEgress, failures);
 3338        ValidateHandleHashing(options.HandleHashing, failures);
 3339        ValidatePermissionGrantBoundary(options.PermissionGrants, failures);
 40
 3341        return failures.Count == 0 ? ValidateOptionsResult.Success : ValidateOptionsResult.Fail(failures);
 42    }
 43
 44    /// <summary>
 45    /// Rejects a grant boundary that cannot be parsed.
 46    /// </summary>
 47    /// <remarks>
 48    /// The boundary is security configuration, so a malformed entry has no safe silent reading: an unparseable
 49    /// allow entry would leave the list empty, which means unrestricted, and an unparseable deny entry would
 50    /// quietly stop denying what it names. Failing startup puts the mistake in front of whoever can fix it
 51    /// instead of leaving it to be discovered from an issued token.
 52    /// </remarks>
 53    private void ValidatePermissionGrantBoundary(PermissionGrantOptions? permissionGrants, ICollection<string> failures)
 54    {
 3355        if (permissionGrants is null)
 056            return;
 57
 3358        ValidatePermissionPatterns(permissionGrants.AllowedPermissions, "AllowedPermissions", failures);
 3359        ValidatePermissionPatterns(permissionGrants.DeniedPermissions, "DeniedPermissions", failures);
 60
 61        // Deny entries match in both directions, so a wildcard grant that could reach a denied permission is
 62        // refused whole rather than narrowed — a role holding '*' does not survive external token issuance.
 63        // That is intended, but surprising enough at sign-in time to be worth announcing at startup.
 3364        if (permissionGrants.DeniedPermissions is { Count: > 0 })
 965            logger.LogWarning(
 966                "ExternalAuthentication:PermissionGrants:DeniedPermissions is configured. Deny entries match wildcard gr
 3367    }
 68
 69    private static void ValidatePermissionPatterns(IEnumerable<string>? permissions, string listName, ICollection<string
 70    {
 17071        foreach (var permission in permissions ?? [])
 72        {
 1973            if (!Permission.TryParse(permission, out var parsed))
 674                failures.Add($"'{permission}' in ExternalAuthentication:PermissionGrants:{listName} is not a well-formed
 75            // An entry like 'workflows*:delete' parses but the matcher never satisfies it, so in a deny
 76            // list it would silently stop denying what it names.
 1377            else if (!parsed.IsValidPattern)
 878                failures.Add($"'{permission}' in ExternalAuthentication:PermissionGrants:{listName} places '*' where it 
 79        }
 6680    }
 81
 82    private static void ValidateExternalCallbackBaseUri(RedirectValidationOptions? redirects, ICollection<string> failur
 83    {
 3384        if (redirects?.ExternalCallbackBaseUri is null)
 3085            return;
 86
 387        if (!ExternalCallbackBaseUriValidator.IsValid(redirects.ExternalCallbackBaseUri, redirects.AllowDevelopmentLoopb
 288            failures.Add(ExternalCallbackBaseUriValidator.ErrorMessage);
 389    }
 90
 91    private static void ValidateHandleHashing(ExternalAuthenticationHandleHashingOptions? handleHashing, ICollection<str
 92    {
 3393        if (handleHashing is null)
 94        {
 095            failures.Add("External Authentication handle-hashing settings are required.");
 096            return;
 97        }
 98
 3399        if (string.IsNullOrWhiteSpace(handleHashing.SharedKeyBase64))
 31100            return;
 101
 2102        byte[]? key = null;
 103        try
 104        {
 2105            key = Convert.FromBase64String(handleHashing.SharedKeyBase64);
 1106            if (key.Length < 32)
 1107                failures.Add("External Authentication HandleHashing:SharedKeyBase64 must contain at least 32 bytes.");
 1108        }
 1109        catch (FormatException)
 110        {
 1111            failures.Add("External Authentication HandleHashing:SharedKeyBase64 must be valid base64.");
 1112        }
 113        finally
 114        {
 2115            if (key is not null)
 1116                System.Security.Cryptography.CryptographicOperations.ZeroMemory(key);
 2117        }
 2118    }
 119
 120    private static void ValidateRateLimits(ExternalAuthenticationRateLimitOptions rateLimits, ICollection<string> failur
 121    {
 396122        foreach (var (name, rule) in new (string Name, RateLimitRule? Rule)[]
 33123        {
 33124            (nameof(rateLimits.Discovery), rateLimits.Discovery),
 33125            (nameof(rateLimits.ExternalInitiation), rateLimits.ExternalInitiation),
 33126            (nameof(rateLimits.LocalInitiation), rateLimits.LocalInitiation),
 33127            (nameof(rateLimits.ProviderCallback), rateLimits.ProviderCallback),
 33128            (nameof(rateLimits.TokenExchange), rateLimits.TokenExchange)
 33129        })
 130        {
 165131            if (rule is null || rule.PermitLimit <= 0 || rule.Window <= TimeSpan.Zero)
 1132                failures.Add($"External Authentication rate limit '{name}' must have a positive permit limit and window.
 133        }
 33134    }
 135
 136    private static void ValidateProviderEgress(ProviderEgressOptions? providerEgress, ICollection<string> failures)
 137    {
 33138        if (providerEgress is null)
 139        {
 0140            failures.Add("External Authentication provider egress settings are required.");
 0141            return;
 142        }
 143
 33144        if (providerEgress.MaximumRedirects < 0)
 1145            failures.Add("External Authentication provider egress maximum redirects must not be negative.");
 33146        if (providerEgress.ConnectTimeout <= TimeSpan.Zero || providerEgress.RequestTimeout <= TimeSpan.Zero)
 1147            failures.Add("External Authentication provider egress timeouts must be positive.");
 33148        if (providerEgress.MaximumDiscoveryResponseBytes <= 0 || providerEgress.MaximumTokenResponseBytes <= 0 || provid
 1149            failures.Add("External Authentication provider egress response-size limits must be positive.");
 150
 68151        foreach (var host in providerEgress.AllowedHosts ?? [])
 152        {
 1153            if (string.IsNullOrWhiteSpace(host) || host.Contains('*') || Uri.CheckHostName(host.TrimEnd('.')) == UriHost
 1154                failures.Add($"External Authentication provider egress allowed host '{host}' is invalid.");
 155        }
 156
 33157        if (providerEgress.ProxyUri is { } proxyUri &&
 33158            (!proxyUri.IsAbsoluteUri ||
 33159             !string.Equals(proxyUri.Scheme, "http", StringComparison.OrdinalIgnoreCase) && !string.Equals(proxyUri.Sche
 33160             !string.IsNullOrEmpty(proxyUri.UserInfo) ||
 33161             !string.IsNullOrEmpty(proxyUri.Fragment) ||
 33162             proxyUri.HostNameType == UriHostNameType.Unknown))
 1163            failures.Add("External Authentication provider egress proxy URI must be an absolute HTTP(S) URI without cred
 33164    }
 165
 166    private static HashSet<string> ValidateExtensionTypes(IEnumerable<string> types, string kind, ICollection<string> fa
 167    {
 132168        var result = new HashSet<string>(StringComparer.Ordinal);
 169
 698170        foreach (var type in types)
 171        {
 217172            if (string.IsNullOrWhiteSpace(type))
 173            {
 0174                failures.Add($"Installed {kind} types must not be empty.");
 0175                continue;
 176            }
 177
 217178            if (!result.Add(type))
 2179                failures.Add($"The installed {kind} type '{type}' is registered more than once.");
 180        }
 181
 132182        return result;
 183    }
 184
 185    private static void ValidateAllowedTypes(IEnumerable<string>? allowedTypes, IReadOnlySet<string> installedTypes, str
 186    {
 132187        var seen = new HashSet<string>(StringComparer.Ordinal);
 188
 630189        foreach (var type in allowedTypes ?? [])
 190        {
 183191            if (string.IsNullOrWhiteSpace(type))
 192            {
 0193                failures.Add($"Allowed {kind} types must not be empty.");
 0194                continue;
 195            }
 196
 183197            if (!seen.Add(type))
 1198                failures.Add($"The allowed {kind} type '{type}' is configured more than once.");
 199
 183200            if (!installedTypes.Contains(type))
 1201                failures.Add($"The allowed {kind} type '{type}' is not installed.");
 202        }
 132203    }
 204
 205    private static void ValidateClients(ExternalAuthenticationOptions options, ICollection<string> failures)
 206    {
 33207        var clientIds = new HashSet<string>(StringComparer.Ordinal);
 208
 72209        foreach (var client in options.Clients ?? [])
 210        {
 3211            if (string.IsNullOrWhiteSpace(client.ClientId))
 0212                failures.Add("Authentication client identifiers must not be empty.");
 3213            else if (!clientIds.Add(client.ClientId))
 0214                failures.Add($"The authentication client identifier '{client.ClientId}' is configured more than once.");
 215
 3216            var callbackUris = client.CallbackUris ?? new HashSet<Uri>();
 3217            var logoutCallbackUris = client.LogoutCallbackUris ?? new HashSet<Uri>();
 3218            var allowedOrigins = client.AllowedOrigins ?? new HashSet<string>();
 3219            var allowedReturnPathPrefixes = client.AllowedReturnPathPrefixes ?? new HashSet<string>();
 220
 3221            if (callbackUris.Count == 0)
 0222                failures.Add($"Authentication client '{client.ClientId}' must register at least one callback URI.");
 223
 3224            ValidateUris(client.ClientId, callbackUris, "callback", options.Redirects.AllowDevelopmentLoopbackCallbacks,
 3225            ValidateUris(client.ClientId, logoutCallbackUris, "logout callback", options.Redirects.AllowDevelopmentLoopb
 3226            ValidateOrigins(client.ClientId, allowedOrigins, options.Redirects.AllowDevelopmentLoopbackCallbacks, failur
 3227            ValidateReturnPathPrefixes(client.ClientId, allowedReturnPathPrefixes, failures);
 228
 3229            if (client.ClientType == AuthenticationClientType.Public)
 230            {
 3231                if (client.SecretBinding is not null)
 2232                    failures.Add($"Public authentication client '{client.ClientId}' must not define a client secret bind
 233
 3234                if (allowedOrigins.Count == 0)
 0235                    failures.Add($"Public authentication client '{client.ClientId}' must register at least one allowed o
 236
 3237                var originSet = allowedOrigins.ToHashSet(StringComparer.Ordinal);
 12238                foreach (var callbackUri in callbackUris)
 239                {
 3240                    if (callbackUri.IsAbsoluteUri && !string.IsNullOrWhiteSpace(callbackUri.Host) && !originSet.Contains
 2241                        failures.Add($"Public authentication client '{client.ClientId}' callback URI '{callbackUri}' doe
 242                }
 243            }
 0244            else if (client.SecretBinding is null)
 0245                failures.Add($"Confidential authentication client '{client.ClientId}' must define a client secret bindin
 246        }
 33247    }
 248
 249    private static void ValidateUris(string clientId, IEnumerable<Uri> uris, string registrationKind, bool allowDevelopm
 250    {
 6251        var registeredUris = new HashSet<string>(StringComparer.Ordinal);
 252
 20253        foreach (var uri in uris)
 254        {
 4255            if (!IsExactClientUri(uri, allowDevelopmentLoopback))
 256            {
 1257                failures.Add($"Authentication client '{clientId}' has an invalid {registrationKind} URI '{uri}'. Registr
 1258                continue;
 259            }
 260
 3261            if (!registeredUris.Add(uri.AbsoluteUri))
 0262                failures.Add($"Authentication client '{clientId}' registers {registrationKind} URI '{uri}' more than onc
 263        }
 6264    }
 265
 266    private static void ValidateOrigins(string clientId, IEnumerable<string> origins, bool allowDevelopmentLoopback, ICo
 267    {
 3268        var registeredOrigins = new HashSet<string>(StringComparer.Ordinal);
 269
 12270        foreach (var origin in origins)
 271        {
 3272            if (!Uri.TryCreate(origin, UriKind.Absolute, out var uri) ||
 3273                !IsExactClientUri(uri, allowDevelopmentLoopback) ||
 3274                uri.AbsolutePath != "/" ||
 3275                uri.Query.Length != 0)
 276            {
 1277                failures.Add($"Authentication client '{clientId}' has invalid allowed origin '{origin}'. Origins must be
 1278                continue;
 279            }
 280
 2281            var normalizedOrigin = uri.GetLeftPart(UriPartial.Authority);
 2282            if (!string.Equals(origin, normalizedOrigin, StringComparison.Ordinal))
 1283                failures.Add($"Authentication client '{clientId}' allowed origin '{origin}' must not contain a path, que
 284
 2285            if (!registeredOrigins.Add(normalizedOrigin))
 0286                failures.Add($"Authentication client '{clientId}' registers allowed origin '{origin}' more than once.");
 287        }
 3288    }
 289
 290    private static void ValidateReturnPathPrefixes(string clientId, IReadOnlySet<string> prefixes, ICollection<string> f
 291    {
 3292        if (prefixes.Count == 0)
 293        {
 1294            failures.Add($"Authentication client '{clientId}' must register at least one allowed return-path prefix.");
 1295            return;
 296        }
 297
 2298        var registeredPrefixes = new HashSet<string>(StringComparer.Ordinal);
 8299        foreach (var prefix in prefixes)
 300        {
 2301            if (!ClientReturnPathValidator.TryValidateForClient(prefix, new HashSet<string>(StringComparer.Ordinal) { pr
 302            {
 0303                failures.Add($"Authentication client '{clientId}' has invalid allowed return-path prefix '{prefix}'.");
 0304                continue;
 305            }
 306
 2307            if (!registeredPrefixes.Add(prefix))
 0308                failures.Add($"Authentication client '{clientId}' registers allowed return-path prefix '{prefix}' more t
 309        }
 2310    }
 311
 312    private static bool IsExactClientUri(Uri uri, bool allowDevelopmentLoopback)
 313    {
 6314        if (!uri.IsAbsoluteUri || !string.IsNullOrEmpty(uri.Fragment) || !string.IsNullOrEmpty(uri.UserInfo) || uri.Host
 0315            return false;
 316
 6317        return string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) ||
 6318            allowDevelopmentLoopback && uri.IsLoopback && string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.
 319    }
 320
 321    private static void ValidateConfigurationConnections(
 322        ExternalAuthenticationOptions options,
 323        IReadOnlySet<string> installedAdapterTypes,
 324        IReadOnlySet<string> installedPolicyTypes,
 325        IReadOnlySet<string> installedGrantSourceTypes,
 326        ICollection<string> failures)
 327    {
 33328        var connectionKeys = new HashSet<string>(StringComparer.Ordinal);
 33329        var configuredConnectionIds = new HashSet<string>(StringComparer.Ordinal);
 33330        var hasPreferredConnection = false;
 331
 80332        foreach (var connection in options.ConfigurationConnections ?? [])
 333        {
 7334            var tenantId = string.IsNullOrWhiteSpace(connection.TenantId) ? ConnectionScope.HostTenantId : connection.Te
 7335            if (tenantId != ConnectionScope.HostTenantId)
 3336                failures.Add($"Configuration connection '{connection.Key}' must use the host scope in this version.");
 7337            var key = connection.Key?.Trim().ToLowerInvariant() ?? string.Empty;
 338
 7339            if (string.IsNullOrWhiteSpace(key))
 0340                failures.Add("Configuration connection keys must not be empty.");
 7341            else if (!connectionKeys.Add(key))
 1342                failures.Add($"Configuration connection key '{connection.Key}' is configured more than once.");
 343
 7344            if (!string.IsNullOrWhiteSpace(connection.Id) && !configuredConnectionIds.Add(connection.Id))
 0345                failures.Add($"Configuration connection ID '{connection.Id}' is configured more than once.");
 346
 7347            if (connection.IsPreferred && hasPreferredConnection)
 1348                failures.Add("Configuration connections define more than one preferred sign-in method.");
 7349            hasPreferredConnection |= connection.IsPreferred;
 350
 7351            if (string.IsNullOrWhiteSpace(connection.AdapterType) || !installedAdapterTypes.Contains(connection.AdapterT
 1352                failures.Add($"Configuration connection '{connection.Key}' selects adapter type '{connection.AdapterType
 6353            else if (options.AllowedAdapterTypes.Count > 0 && !options.AllowedAdapterTypes.Contains(connection.AdapterTy
 0354                failures.Add($"Configuration connection '{connection.Key}' selects adapter type '{connection.AdapterType
 355
 7356            if (connection.AdapterSettingsVersion <= 0)
 0357                failures.Add($"Configuration connection '{connection.Key}' must use a positive adapter settings version.
 358
 7359            if (string.IsNullOrWhiteSpace(connection.DisplayName))
 0360                failures.Add($"Configuration connection '{connection.Key}' must define a display name.");
 361
 7362            ValidatePolicy(connection, options, installedPolicyTypes, failures);
 7363            ValidateGrantSources(connection, options, installedGrantSourceTypes, failures);
 364        }
 365
 33366    }
 367
 368    private static void ValidatePolicy(IdentityProviderConnection connection, ExternalAuthenticationOptions options, IRe
 369    {
 7370        var policy = connection.UnlinkedPolicy;
 7371        if (policy is null)
 7372            return;
 373
 0374        if (policy.SettingsVersion <= 0)
 0375            failures.Add($"Configuration connection '{connection.Key}' must use a positive unlinked identity policy sett
 376
 0377        if (!installedPolicyTypes.Contains(policy.Type))
 0378            failures.Add($"Configuration connection '{connection.Key}' selects unlinked identity policy '{policy.Type}',
 0379        else if (options.AllowedUnlinkedIdentityPolicyTypes.Count > 0 && !options.AllowedUnlinkedIdentityPolicyTypes.Con
 0380            failures.Add($"Configuration connection '{connection.Key}' selects unlinked identity policy '{policy.Type}',
 0381    }
 382
 383    private static void ValidateGrantSources(IdentityProviderConnection connection, ExternalAuthenticationOptions option
 384    {
 7385        var orders = new HashSet<int>();
 14386        foreach (var grantSource in connection.PermissionGrantSources ?? [])
 387        {
 0388            if (!orders.Add(grantSource.Order))
 0389                failures.Add($"Configuration connection '{connection.Key}' configures more than one permission grant sou
 390
 0391            if (grantSource.SettingsVersion <= 0)
 0392                failures.Add($"Configuration connection '{connection.Key}' must use a positive permission grant source s
 393
 0394            if (!installedGrantSourceTypes.Contains(grantSource.Type))
 0395                failures.Add($"Configuration connection '{connection.Key}' selects permission grant source '{grantSource
 0396            else if (options.AllowedPermissionGrantSourceTypes.Count > 0 && !options.AllowedPermissionGrantSourceTypes.C
 0397                failures.Add($"Configuration connection '{connection.Key}' selects permission grant source '{grantSource
 398        }
 7399    }
 400}

Methods/Properties

.ctor(Microsoft.Extensions.Options.IOptions`1<Elsa.ExternalAuthentication.Options.ExternalAuthenticationExtensionOptions>,Microsoft.Extensions.Logging.ILogger`1<Elsa.ExternalAuthentication.Validation.ExternalAuthenticationOptionsValidator>)
Validate(System.String,Elsa.ExternalAuthentication.Options.ExternalAuthenticationOptions)
ValidatePermissionGrantBoundary(Elsa.ExternalAuthentication.Options.PermissionGrantOptions,System.Collections.Generic.ICollection`1<System.String>)
ValidatePermissionPatterns(System.Collections.Generic.IEnumerable`1<System.String>,System.String,System.Collections.Generic.ICollection`1<System.String>)
ValidateExternalCallbackBaseUri(Elsa.ExternalAuthentication.Options.RedirectValidationOptions,System.Collections.Generic.ICollection`1<System.String>)
ValidateHandleHashing(Elsa.ExternalAuthentication.Options.ExternalAuthenticationHandleHashingOptions,System.Collections.Generic.ICollection`1<System.String>)
ValidateRateLimits(Elsa.ExternalAuthentication.Options.ExternalAuthenticationRateLimitOptions,System.Collections.Generic.ICollection`1<System.String>)
ValidateProviderEgress(Elsa.ExternalAuthentication.Options.ProviderEgressOptions,System.Collections.Generic.ICollection`1<System.String>)
ValidateExtensionTypes(System.Collections.Generic.IEnumerable`1<System.String>,System.String,System.Collections.Generic.ICollection`1<System.String>)
ValidateAllowedTypes(System.Collections.Generic.IEnumerable`1<System.String>,System.Collections.Generic.IReadOnlySet`1<System.String>,System.String,System.Collections.Generic.ICollection`1<System.String>)
ValidateClients(Elsa.ExternalAuthentication.Options.ExternalAuthenticationOptions,System.Collections.Generic.ICollection`1<System.String>)
ValidateUris(System.String,System.Collections.Generic.IEnumerable`1<System.Uri>,System.String,System.Boolean,System.Collections.Generic.ICollection`1<System.String>)
ValidateOrigins(System.String,System.Collections.Generic.IEnumerable`1<System.String>,System.Boolean,System.Collections.Generic.ICollection`1<System.String>)
ValidateReturnPathPrefixes(System.String,System.Collections.Generic.IReadOnlySet`1<System.String>,System.Collections.Generic.ICollection`1<System.String>)
IsExactClientUri(System.Uri,System.Boolean)
ValidateConfigurationConnections(Elsa.ExternalAuthentication.Options.ExternalAuthenticationOptions,System.Collections.Generic.IReadOnlySet`1<System.String>,System.Collections.Generic.IReadOnlySet`1<System.String>,System.Collections.Generic.IReadOnlySet`1<System.String>,System.Collections.Generic.ICollection`1<System.String>)
ValidatePolicy(Elsa.ExternalAuthentication.Models.IdentityProviderConnection,Elsa.ExternalAuthentication.Options.ExternalAuthenticationOptions,System.Collections.Generic.IReadOnlySet`1<System.String>,System.Collections.Generic.ICollection`1<System.String>)
ValidateGrantSources(Elsa.ExternalAuthentication.Models.IdentityProviderConnection,Elsa.ExternalAuthentication.Options.ExternalAuthenticationOptions,System.Collections.Generic.IReadOnlySet`1<System.String>,System.Collections.Generic.ICollection`1<System.String>)