/// <summary>
        /// Initializes a new instance of the <see cref="MobileServiceAuthentication"/> class.
        /// </summary>
        /// <param name="client">
        /// The <see cref="MobileServiceClient"/> associated with this
        /// MobileServiceLogin instance.
        /// </param>
        /// <param name="providerName">
        /// The <see cref="MobileServiceAuthenticationProvider"/> used to authenticate.
        /// </param>
        /// <param name="parameters">
        /// Provider specific extra parameters that are sent as query string parameters to login endpoint.
        /// </param>
        public MobileServiceAuthentication(IMobileServiceClient client, string providerName, IDictionary <string, string> parameters)
        {
            Arguments.IsNotNull(client, nameof(client));
            Arguments.IsNotNull(providerName, nameof(providerName));

            this.Client       = client;
            this.Parameters   = parameters;
            this.ProviderName = providerName;
            string path = MobileServiceUrlBuilder.CombinePaths(LoginAsyncUriFragment, this.ProviderName);
            string loginAsyncDoneUriFragment = MobileServiceAuthentication.LoginAsyncDoneUriFragment;

            if (!string.IsNullOrEmpty(this.Client.LoginUriPrefix))
            {
                path = MobileServiceUrlBuilder.CombinePaths(this.Client.LoginUriPrefix, this.ProviderName);
                loginAsyncDoneUriFragment = MobileServiceUrlBuilder.CombinePaths(this.Client.LoginUriPrefix, "done");
            }
            string queryString  = MobileServiceUrlBuilder.GetQueryString(parameters, useTableAPIRules: false);
            string pathAndQuery = MobileServiceUrlBuilder.CombinePathAndQuery(path, queryString);

            this.StartUri = new Uri(this.Client.MobileAppUri, pathAndQuery);
            this.EndUri   = new Uri(this.Client.MobileAppUri, loginAsyncDoneUriFragment);

            if (this.Client.AlternateLoginHost != null)
            {
                this.StartUri = new Uri(this.Client.AlternateLoginHost, pathAndQuery);
                this.EndUri   = new Uri(this.Client.AlternateLoginHost, loginAsyncDoneUriFragment);
            }
        }
        /// <summary>
        /// Provides Login logic for an existing token.
        /// </summary>
        /// <returns>
        /// Task that will complete with the response string when the user has finished authentication.
        /// </returns>
        public override Task <string> LoginAsyncOverride()
        {
            string path = string.IsNullOrEmpty(client.LoginUriPrefix)
                ? MobileServiceUrlBuilder.CombinePaths(LoginAsyncUriFragment, ProviderName)
                : MobileServiceUrlBuilder.CombinePaths(client.LoginUriPrefix, ProviderName);
            string queryString  = MobileServiceUrlBuilder.GetQueryString(Parameters);
            string pathAndQuery = MobileServiceUrlBuilder.CombinePathAndQuery(path, queryString);

            return(client.AlternateLoginHost != null
                ? client.AlternateAuthHttpClient.RequestWithoutHandlersAsync(HttpMethod.Post, pathAndQuery, client.CurrentUser, token.ToString())
                : client.HttpClient.RequestWithoutHandlersAsync(HttpMethod.Post, pathAndQuery, client.CurrentUser, token.ToString()));
        }
        /// <summary>
        /// Login via OAuth 2.0 PKCE protocol.
        /// </summary>
        /// <returns></returns>
        public sealed override async Task <string> LoginAsyncOverride()
        {
            // Show platform-specific login ui and care about handling authorization_code from callback via deep linking.
            var authorizationCode = await GetAuthorizationCodeAsync();

            // Send authorization_code and code_verifier via HTTPS request to complete the PKCE flow.
            var path = string.IsNullOrEmpty(Client.LoginUriPrefix)
                ? MobileServiceUrlBuilder.CombinePaths(LoginAsyncUriFragment, ProviderName)
                : MobileServiceUrlBuilder.CombinePaths(this.Client.LoginUriPrefix, ProviderName);

            path = MobileServiceUrlBuilder.CombinePaths(path, "token");
            var tokenParameters = Parameters != null ? new Dictionary <string, string>(Parameters) : new Dictionary <string, string>();

            tokenParameters.Add("authorization_code", authorizationCode);
            tokenParameters.Add("code_verifier", CodeVerifier);
            var queryString  = MobileServiceUrlBuilder.GetQueryString(tokenParameters);
            var pathAndQuery = MobileServiceUrlBuilder.CombinePathAndQuery(path, queryString);
            var httpClient   = client.AlternateLoginHost == null ? client.HttpClient : client.AlternateAuthHttpClient;

            return(await httpClient.RequestWithoutHandlersAsync(HttpMethod.Get, pathAndQuery, null));
        }
        /// <summary>
        /// Create a new instance of the <see cref="MobileServicePKCEAuthentication"/>.
        /// </summary>
        /// <param name="client">The <see cref="MobileServiceClient"/> to use for communication.</param>
        /// <param name="provider">The authentication provider.</param>
        /// <param name="uriScheme">The URI Scheme</param>
        /// <param name="parameters">The parameters to send along with the request</param>
        public MobileServicePKCEAuthentication(MobileServiceClient client, string provider, string uriScheme, IDictionary <string, string> parameters)
            : base(client, provider, parameters)
        {
            Arguments.IsNotNull(client, nameof(client));
            Arguments.IsNotNullOrWhiteSpace(uriScheme, nameof(uriScheme));

            this.client  = client;
            CodeVerifier = GetCodeVerifier();
            CallbackUri  = new Uri(MobileServiceUrlBuilder.CombileSchemeAndPath(uriScheme, "easyauth.callback"));

            var path = string.IsNullOrEmpty(Client.LoginUriPrefix)
                ? MobileServiceUrlBuilder.CombinePaths(LoginAsyncUriFragment, ProviderName)
                : MobileServiceUrlBuilder.CombinePaths(this.Client.LoginUriPrefix, ProviderName);
            var loginParameters = parameters != null ? new Dictionary <string, string>(parameters) : new Dictionary <string, string>();

            loginParameters.Add("post_login_redirect_url", this.CallbackUri.AbsoluteUri);
            loginParameters.Add("code_challenge", GetSha256Hash(this.CodeVerifier));
            loginParameters.Add("code_challenge_method", "S256");
            loginParameters.Add("session_mode", "token");
            var loginQueryString  = MobileServiceUrlBuilder.GetQueryString(loginParameters, false);
            var loginPathAndQuery = MobileServiceUrlBuilder.CombinePathAndQuery(path, loginQueryString);

            LoginUri = new Uri(Client.AlternateLoginHost ?? Client.MobileAppUri, loginPathAndQuery);
        }