/// <summary>
    /// Parses a URL and turns it into authority and discovery endpoint URL.
    /// </summary>
    /// <param name="input">The input.</param>
    /// <param name="path">The path to the discovery document. If not specified this defaults to .well-known/open-id-configuration</param>
    /// <returns></returns>
    /// <exception cref="System.InvalidOperationException">
    /// Malformed URL
    /// </exception>
    public static DiscoveryEndpoint ParseUrl(string input, string path = null)
    {
        if (String.IsNullOrEmpty(path))
        {
            path = OidcConstants.Discovery.DiscoveryEndpoint;
        }

        var success = Uri.TryCreate(input, UriKind.Absolute, out var uri);

        if (success == false)
        {
            throw new InvalidOperationException("Malformed URL");
        }

        if (!DiscoveryEndpoint.IsValidScheme(uri))
        {
            throw new InvalidOperationException("Malformed URL");
        }

        var url = input.RemoveTrailingSlash();

        if (path.StartsWith("/"))
        {
            path = path.Substring(1);
        }

        if (url.EndsWith(path, StringComparison.OrdinalIgnoreCase))
        {
            return(new DiscoveryEndpoint(url.Substring(0, url.Length - path.Length - 1), url));
        }
        else
        {
            return(new DiscoveryEndpoint(url, url.EnsureTrailingSlash() + path));
        }
    }
    /// <summary>
    /// Validates the endoints and jwks_uri according to the security policy.
    /// </summary>
    /// <param name="json">The json.</param>
    /// <param name="policy">The policy.</param>
    /// <returns></returns>
    public string ValidateEndpoints(JsonElement json, DiscoveryPolicy policy)
    {
        // allowed hosts
        var allowedHosts = new HashSet <string>(policy.AdditionalEndpointBaseAddresses.Select(e => new Uri(e).Authority))
        {
            new Uri(policy.Authority).Authority
        };

        // allowed authorities (hosts + base address)
        var allowedAuthorities = new HashSet <string>(policy.AdditionalEndpointBaseAddresses)
        {
            policy.Authority
        };

        foreach (var element in json.EnumerateObject())
        {
            if (element.Name.EndsWith("endpoint", StringComparison.OrdinalIgnoreCase) ||
                element.Name.Equals(OidcConstants.Discovery.JwksUri, StringComparison.OrdinalIgnoreCase) ||
                element.Name.Equals(OidcConstants.Discovery.CheckSessionIframe, StringComparison.OrdinalIgnoreCase))
            {
                var endpoint = element.Value.ToString();

                var isValidUri = Uri.TryCreate(endpoint, UriKind.Absolute, out Uri uri);
                if (!isValidUri)
                {
                    return($"Malformed endpoint: {endpoint}");
                }

                if (!DiscoveryEndpoint.IsValidScheme(uri))
                {
                    return($"Malformed endpoint: {endpoint}");
                }

                if (!DiscoveryEndpoint.IsSecureScheme(uri, policy))
                {
                    return($"Endpoint does not use HTTPS: {endpoint}");
                }

                if (policy.ValidateEndpoints)
                {
                    // if endpoint is on exclude list, don't validate
                    if (policy.EndpointValidationExcludeList.Contains(element.Name))
                    {
                        continue;
                    }

                    bool isAllowed = false;
                    foreach (var host in allowedHosts)
                    {
                        if (string.Equals(host, uri.Authority))
                        {
                            isAllowed = true;
                        }
                    }

                    if (!isAllowed)
                    {
                        return($"Endpoint is on a different host than authority: {endpoint}");
                    }

                    IAuthorityValidationStrategy strategy = policy.AuthorityValidationStrategy ?? DiscoveryPolicy.DefaultAuthorityValidationStrategy;
                    AuthorityValidationResult    endpointValidationResult = strategy.IsEndpointValid(endpoint, allowedAuthorities);
                    if (!endpointValidationResult.Success)
                    {
                        return(endpointValidationResult.ErrorMessage);
                    }
                }
            }
        }

        if (policy.RequireKeySet)
        {
            if (string.IsNullOrWhiteSpace(JwksUri))
            {
                return("Keyset is missing");
            }
        }

        return(string.Empty);
    }