protected MobileServicePKCEAuthentication(MobileServiceClient client, string provider, string uriScheme, IDictionary <string, string> parameters)
            : base(client, provider, parameters)
        {
            if (client == null)
            {
                throw new ArgumentNullException("client");
            }
            if (string.IsNullOrWhiteSpace(uriScheme))
            {
                throw new ArgumentException("uriScheme");
            }

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

            var path = MobileServiceUrlBuilder.CombinePaths(LoginAsyncUriFragment, this.ProviderName);

            if (!string.IsNullOrEmpty(this.Client.LoginUriPrefix))
            {
                path = MobileServiceUrlBuilder.CombinePaths(this.Client.LoginUriPrefix, this.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);

            this.LoginUri = new Uri(this.Client.MobileAppUri, loginPathAndQuery);
            if (this.Client.AlternateLoginHost != null)
            {
                this.LoginUri = new Uri(this.Client.AlternateLoginHost, loginPathAndQuery);
            }
        }