/// <summary>
        /// Gets a Salesforce token based on the refresh token and adds it to the cache.
        /// </summary>
        /// <param name="salesforceToken">The Salesforce token that contains the refresh token to use.</param>
        /// <returns>The asynchronous task.</returns>
        private static async Task AcquireAccessTokenFromRefreshTokenAsync(SalesforceToken salesforceToken)
        {
            // Remove the old token from the cache
            HttpContext.Current.Session[SalesforceService.TokenCacheKey] = null;

            try
            {
                using (AuthenticationClient authenticationClient = salesforceToken.GetAuthenticationClient())
                {
                    // Attempt to refresh the token
                    await authenticationClient.TokenRefreshAsync(
                        SalesforceService.GetAppSetting("Salesforce:ConsumerKey"),
                        salesforceToken.RefreshToken,
                        string.Empty,
                        SalesforceService.GetAppSetting("Salesforce:Domain") + "/services/oauth2/token");

                    salesforceToken = new SalesforceToken(authenticationClient);

                    // Add the new token to the cache
                    HttpContext.Current.Session[SalesforceService.TokenCacheKey] = salesforceToken;
                }
            }
            catch (ForceException e)
            {
                // InvalidGrant means that the refresh token is invalid.
                // Just return in that case, so re-authorization can occur.
                if (e.Error != Error.InvalidGrant)
                {
                    throw;
                }
            }
        }
        /// <summary>
        /// Makes a request to the Salesforce client, wrapping the specified logic with the code necessary to handle authentication.
        /// </summary>
        /// <typeparam name="T">The type of value returned from the request.</typeparam>
        /// <param name="request">The request to make on the Salesforce client.</param>
        /// <exception cref="InvalidOperationException">The current user is not authenticated.</exception>
        /// <returns>The value returned by the request.</returns>
        public static async Task <T> MakeAuthenticatedClientRequestAsync <T>(Func <ForceClient, Task <T> > request)
        {
            bool done           = false;
            bool refreshedToken = false;
            T    result         = default(T);

            do
            {
                // 1. Get the token from the cache
                SalesforceToken salesforceToken = HttpContext.Current.Session[SalesforceService.TokenCacheKey] as SalesforceToken;

                // 2. If no token is available, redirect to authorize
                if (salesforceToken == null)
                {
                    // This exception message should be used to trigger the sign-in UI
                    throw new InvalidOperationException("AuthorizationRequired");
                }

                // Initialize the ForceClient
                ForceClient forceClient = salesforceToken.GetForceClient();

                try
                {
                    // 3. Invoke the request with the acquired token
                    result = await request(forceClient);

                    done = true;
                }
                catch (ForceException e)
                {
                    // If message is "Session expired or invalid", the access token is invalid, so eat the
                    // exception and fall through to try to update it using the refresh token.
                    if (e.Message != "Session expired or invalid")
                    {
                        throw;
                    }

                    if (refreshedToken)
                    {
                        // The access token is invalid and was already refreshed once, give up and
                        // re-throw the exception.
                        throw;
                    }
                }

                // 4. If the token is invalid, attempt to acquire a new access token from the refresh token
                if (!done)
                {
                    await SalesforceService.AcquireAccessTokenFromRefreshTokenAsync(salesforceToken);

                    refreshedToken = true;
                }
            } while (!done);

            return(result);
        }
 /// <summary>
 /// Gets the Salesforce authorization URL.  The optional target parameter allows the app to redirect to
 /// a specified page after authorization; if the parameter is not specified, the app redirects to the current
 /// request's URL.
 /// </summary>
 /// <returns>The Salesforce authorization URL.</returns>
 public static string GetAuthorizationUrl(string targetUri = null)
 {
     return(Common.FormatAuthUrl(
                SalesforceService.GetAppSetting("Salesforce:Domain") + "/services/oauth2/authorize",
                ResponseTypes.Code,
                SalesforceService.GetAppSetting("Salesforce:ConsumerKey"),
                HttpUtility.UrlEncode(SalesforceOAuthRedirectHandler.GetAbsoluteRedirectUri()),
                DisplayTypes.Page,
                false,
                HttpUtility.UrlEncode(string.IsNullOrEmpty(targetUri) ? HttpContext.Current.Request.Url.AbsoluteUri : targetUri)));
 }
        /// <summary>
        /// Processes the authentication callback from Salesforce.
        /// </summary>
        /// <param name="context">The HTTP context.</param>
        /// <returns>The asynchronous task.</returns>
        public override async Task ProcessRequestAsync(HttpContext context)
        {
            await SalesforceService.AcquireTokenByAuthorizationCodeAsync(
                context.Request.QueryString["code"],
                SalesforceOAuthRedirectHandler.GetAbsoluteRedirectUri());

            string state       = HttpUtility.UrlDecode(context.Request.QueryString["state"]);
            string redirectUrl = state == null ? "~/" : state;

            context.Response.Redirect(redirectUrl, false);
        }
        /// <summary>
        /// Gets the absolute Uri to redirect to after Salesforce has completed authenticating a user.  This is the Uri
        /// to this handler.
        /// </summary>
        /// <returns>The absolute redirect Uri.</returns>
        private static string GetAbsoluteRedirectUri()
        {
            Uri redirectUri;

            Uri.TryCreate(SalesforceService.GetAppSetting("Salesforce:RedirectUri"), UriKind.RelativeOrAbsolute, out redirectUri);
            if (redirectUri.IsAbsoluteUri)
            {
                return(redirectUri.ToString());
            }
            else
            {
                string uriAuthority = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
                return(new Uri(new Uri(uriAuthority), redirectUri).ToString());
            }
        }
        /// <summary>
        /// Gets a Salesforce token from an authorization code and adds it to the cache.
        /// </summary>
        /// <param name="authorizationCode">The code that was returned to the OAuth callback.</param>
        /// <param name="redirectUri">The redirect URI that was used to acquire the code.</param>
        /// <returns>The asynchronous task.</returns>
        public static async Task AcquireTokenByAuthorizationCodeAsync(string authorizationCode, string redirectUri)
        {
            using (AuthenticationClient authenticationClient = new AuthenticationClient())
            {
                await authenticationClient.WebServerAsync(
                    SalesforceService.GetAppSetting("Salesforce:ConsumerKey"),
                    SalesforceService.GetAppSetting("Salesforce:ConsumerSecret"),
                    redirectUri,
                    authorizationCode,
                    SalesforceService.GetAppSetting("Salesforce:Domain") + "/services/oauth2/token");

                SalesforceToken salesforceToken = new SalesforceToken(authenticationClient);

                // Add the new token to the cache
                HttpContext.Current.Session[SalesforceService.TokenCacheKey] = salesforceToken;
            }
        }