public static async Task <ProviderInformation> LoadFromMetadataAsync(string authority)
        {
            var client = new HttpClient();
            var url    = authority.EnsureTrailingSlash() + ".well-known/openid-configuration";

            var json = await client.GetStringAsync(url);

            var doc = JsonConvert.DeserializeObject <Dictionary <string, object> >(json);

            var info = new ProviderInformation
            {
                IssuerName        = doc["issuer"].ToString(),
                AuthorizeEndpoint = doc["authorization_endpoint"].ToString(),
                TokenEndpoint     = doc["token_endpoint"].ToString(),
            };

            if (doc.ContainsKey("end_session_endpoint"))
            {
                info.EndSessionEndpoint = doc["end_session_endpoint"].ToString();
            }

            if (doc.ContainsKey("userinfo_endpoint"))
            {
                info.UserInfoEndpoint = doc["userinfo_endpoint"].ToString();
            }

            // parse web key set
            var jwksUri = doc["jwks_uri"].ToString();
            var jwks    = await client.GetStringAsync(jwksUri);

            info.KeySet = new JsonWebKeySet(jwks);

            return(info);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="OidcClientOptions"/> class.
        /// </summary>
        /// <param name="authority">The authority.</param>
        /// <param name="clientId">The client identifier.</param>
        /// <param name="clientSecret">The client secret.</param>
        /// <param name="scope">The scope.</param>
        /// <param name="redirectUri">The redirect URI.</param>
        /// <param name="webView">The web view.</param>
        /// <param name="validator">The validator.</param>
        /// <exception cref="System.ArgumentNullException">authority</exception>
        public OidcClientOptions(string authority, string clientId, string clientSecret, string scope, string redirectUri, IWebView webView = null, IIdentityTokenValidator validator = null)
            : this(clientId, clientSecret, scope, redirectUri, webView, validator)
        {
            if (string.IsNullOrWhiteSpace(authority))
            {
                throw new ArgumentNullException(nameof(authority));
            }

            _providerInfo = new Lazy <Task <ProviderInformation> >(async() => await ProviderInformation.LoadFromMetadataAsync(authority, ValidateIssuerName, BackchannelHandler, (int)BackchannelTimeout.TotalSeconds));
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="OidcClientOptions"/> class.
        /// </summary>
        /// <param name="info">The provider information.</param>
        /// <param name="clientId">The client id.</param>
        /// <param name="clientSecret">The client secret.</param>
        /// <param name="scope">The scope.</param>
        /// <param name="redirectUri">The redirect URI.</param>
        /// <param name="webView">The web view.</param>
        /// <param name="validator">The validator.</param>
        /// <exception cref="System.ArgumentNullException">info</exception>
        public OidcClientOptions(ProviderInformation info, string clientId, string clientSecret, string scope, string redirectUri, IWebView webView = null, IIdentityTokenValidator validator = null)
            : this(clientId, clientSecret, scope, redirectUri, webView, validator)
        {
            if (info == null)
            {
                throw new ArgumentNullException(nameof(info));
            }
            info.Validate();

            _providerInfo = new Lazy <Task <ProviderInformation> >(() => Task.FromResult(info));
        }
Exemplo n.º 4
0
        public async Task <IdentityTokenValidationResult> ValidateAsync(string identityToken, string clientId, ProviderInformation providerInformation)
        {
            var client = new HttpClient();

            var form = new Dictionary <string, string>
            {
                { "token", identityToken },
                { "client_id", clientId }
            };

            var response = await client.PostAsync(
                new Uri(_endpoint),
                new FormUrlEncodedContent(form));

            if (!response.IsSuccessStatusCode)
            {
                return(new IdentityTokenValidationResult
                {
                    Error = response.ReasonPhrase
                });
            }

            var json = JObject.Parse(await response.Content.ReadAsStringAsync());

            return(new IdentityTokenValidationResult
            {
                Claims = json.ToClaims(),
                SignatureAlgorithm = "RS256"
            });
        }
        /// <summary>
        /// Loads from metadata.
        /// </summary>
        /// <param name="authority">The authority.</param>
        /// <param name="validateIssuerName">if set to <c>true</c> the issuer name gets validated against the authority.</param>
        /// <returns>Provider information</returns>
        /// <exception cref="System.InvalidOperationException">
        /// </exception>
        public static async Task <ProviderInformation> LoadFromMetadataAsync(string authority, bool validateIssuerName = true, HttpMessageHandler innerHandler = null, int timeout = 30)
        {
            var handler = innerHandler ?? new HttpClientHandler();
            var client  = new HttpClient(handler);

            client.Timeout = TimeSpan.FromSeconds(timeout);

            var url = authority.EnsureTrailingSlash() + ".well-known/openid-configuration";

            Logger.Debug($"fetching discovery document from: {url}");

            var response = await client.GetAsync(url).ConfigureAwait(false);

            if (!response.IsSuccessStatusCode)
            {
                var error = $"an error occurred while retrieving the discovery document ({url}): " +
                            await FormatErrorAsync(response).ConfigureAwait(false);

                Logger.Error(error);
                throw new InvalidOperationException(error);
            }

            var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

            var doc  = JsonConvert.DeserializeObject <Dictionary <string, object> >(json);
            var info = new ProviderInformation();

            // issuer is required
            if (doc.ContainsKey("issuer"))
            {
                info.IssuerName = doc["issuer"].ToString();
                Logger.Debug($"issuer name: {info.IssuerName}");
            }
            else
            {
                var error = "issuer name is missing in discovery doc.";

                Logger.Error(error);
                throw new InvalidOperationException(error);
            }

            // validate issuer name against authority, if requested
            if (validateIssuerName)
            {
                if (!string.Equals(authority.RemoveTrailingSlash(), info.IssuerName.RemoveTrailingSlash(), StringComparison.OrdinalIgnoreCase))
                {
                    var error = $"issuer name of '{info.IssuerName}' does not match authority '{authority}'";

                    Logger.Error(error);
                    throw new InvalidOperationException(error);
                }
            }

            // authorize endpoint is required
            if (doc.ContainsKey("authorization_endpoint"))
            {
                info.AuthorizeEndpoint = doc["authorization_endpoint"].ToString();
                Logger.Debug($"authorization endpoint: {info.AuthorizeEndpoint}");
            }
            else
            {
                var error = "authorization endpoint is missing in discovery doc.";

                Logger.Error(error);
                throw new InvalidOperationException(error);
            }

            // token endpoint is required
            if (doc.ContainsKey("token_endpoint"))
            {
                info.TokenEndpoint = doc["token_endpoint"].ToString();
                Logger.Debug($"token endpoint: {info.TokenEndpoint}");
            }
            else
            {
                var error = "token endpoint is missing in discovery doc.";

                Logger.Error(error);
                throw new InvalidOperationException(error);
            }

            // end_session endpoint is optional
            if (doc.ContainsKey("end_session_endpoint"))
            {
                info.EndSessionEndpoint = doc["end_session_endpoint"].ToString();
                Logger.Debug($"end_session endpoint: {info.EndSessionEndpoint}");
            }
            else
            {
                Logger.Debug("no end_session endpoint");
            }

            // userinfo endpoint is optional, but required for the load profile feature
            if (doc.ContainsKey("userinfo_endpoint"))
            {
                info.UserInfoEndpoint = doc["userinfo_endpoint"].ToString();
                Logger.Debug($"userinfo_endpoint: {info.UserInfoEndpoint}");
            }
            else
            {
                Logger.Debug("no userinfo_endpoint");
            }

            if (doc.ContainsKey("token_endpoint_auth_methods_supported"))
            {
                info.TokenEndPointAuthenticationMethods =
                    ((JArray)doc["token_endpoint_auth_methods_supported"]).Select(x => (string)x).ToArray();
            }

            // parse web key set
            if (doc.ContainsKey("jwks_uri"))
            {
                var jwksUri = doc["jwks_uri"].ToString();

                var jwksResponse = await client.GetAsync(jwksUri).ConfigureAwait(false);

                if (!jwksResponse.IsSuccessStatusCode)
                {
                    var error = $"an error occurred while retrieving the JWKS document ({jwksUri}) : " +
                                await FormatErrorAsync(jwksResponse).ConfigureAwait(false);

                    Logger.Error(error);
                    throw new InvalidOperationException(error);
                }

                var jwks = await jwksResponse.Content.ReadAsStringAsync().ConfigureAwait(false);

                Logger.Debug($"jwks: {jwks}");
                info.KeySet = new JsonWebKeySet(jwks);
            }
            else
            {
                var error = "jwks_uri is missing in discovery doc.";

                Logger.Error(error);
                throw new InvalidOperationException(error);
            }

            return(info);
        }