/// <summary>
        /// Try to get a new access token for this resource using a refresh token.
        /// If successful, this method will cache the access token for future use.
        /// If this fails, return null, signaling the caller to do the OAuth redirect.
        /// </summary>
        private static string GetAccessTokenFromRefreshToken(string resourceId)
        {
            string refreshToken = Office365Cache.GetRefreshToken().Value;

            if (refreshToken == null)
            {
                // If no refresh token, the caller will need to send the user to do an OAuth redirect.
                return(null);
            }

            // Redeem the refresh token for an access token:
            try
            {
                ClientCredential      credential  = new ClientCredential(AppPrincipalId, AppKey);
                string                authority   = string.Format(CultureInfo.InvariantCulture, OAuthUrl, "common");
                AuthenticationContext authContext = new AuthenticationContext(authority);
                AuthenticationResult  result      = authContext.AcquireTokenByRefreshToken(
                    refreshToken, AppPrincipalId, credential, resourceId);

                // Cache the access token and update the refresh token:
                Office365Cache.GetAccessToken(resourceId).Value = result.AccessToken;
                Office365Cache.GetRefreshToken().Value = result.RefreshToken;

                return(result.AccessToken);
            }
            catch (ActiveDirectoryAuthenticationException)
            {
                // Forget the refresh token and return null, so as to start the OAuth redirect from scratch.
                Office365Cache.GetRefreshToken().RemoveFromCache();
                return(null);
            }
        }
        /// <summary>
        /// Send an HTTP request, with authorization. If the request fails due to an unauthorized exception,
        ///     this method will try to renew the access token in serviceInfo and try again.
        /// </summary>
        public static async Task <HttpResponseMessage> SendRequestAsync(
            Office365ServiceInfo serviceInfo, HttpClient client, Func <HttpRequestMessage> requestCreator)
        {
            using (HttpRequestMessage request = requestCreator.Invoke())
            {
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", serviceInfo.AccessToken);
                request.Headers.UserAgent.Add(new ProductInfoHeaderValue(AppPrincipalId, String.Empty));
                HttpResponseMessage response = await client.SendAsync(request);

                // Check if the server responded with "Unauthorized". If so, it might be a real authorization issue, or
                //     it might be due to an expired access token. To be sure, renew the token and try one more time:
                if (response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    Office365Cache.GetAccessToken(serviceInfo.ResourceId).RemoveFromCache();
                    serviceInfo.AccessToken = GetAccessTokenFromRefreshToken(serviceInfo.ResourceId);

                    // Create and send a new request:
                    using (HttpRequestMessage retryRequest = requestCreator.Invoke())
                    {
                        retryRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", serviceInfo.AccessToken);
                        retryRequest.Headers.UserAgent.Add(new ProductInfoHeaderValue(AppPrincipalId, String.Empty));
                        response = await client.SendAsync(retryRequest);
                    }
                }

                // Return either the original response, or the response from the second attempt:
                return(response);
            }
        }
        /// <summary>
        /// Clears any OAuth-related data, such as access and refresh tokens and state cookies.
        /// This method should be called as part of your application's logout routine.
        /// </summary>
        public static void ClearSession()
        {
            Office365Cache.RemoveAllFromCache();

            // Also remove the cookies used to store the OAuth request state:
            foreach (string cookieName in System.Web.HttpContext.Current.Request.Cookies.AllKeys)
            {
                if (cookieName.StartsWith(OAuthRequestStateCookiePrefix, StringComparison.Ordinal))
                {
                    System.Web.HttpContext.Current.Response.Cookies[cookieName].Expires = DateTime.Now.AddDays(-1);
                }
            }
        }
        /// <summary>
        /// Clears any OAuth-related data, such as access and refresh tokens, and logs out.
        /// This method should be called as part of your application's logout routine.
        /// </summary>
        public static async Task ClearSession()
        {
            string authority = string.Format(CultureInfo.InvariantCulture, OAuthUrl, TenantId ?? "common");
            AuthenticationContext authContext = new AuthenticationContext(authority);

            authContext.TokenCacheStore.Clear();

            Office365Cache.RemoveAllFromCache();

            string requestUrl = string.Format(CultureInfo.InvariantCulture,
                                              LogoutUrl,
                                              TenantId ?? "common",
                                              Uri.EscapeDataString(WebAuthenticationBroker.GetCurrentApplicationCallbackUri().ToString()));
            await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.SilentMode, new Uri(requestUrl));
        }
        /// <summary>
        /// Obtains an access token for the specified resource, using a cached access token or a refresh token.
        /// If successful, the token will be cached for future use.
        /// On failure, this method will return null to signify that the caller must do an OAuth redirect instead.
        /// </summary>
        internal static string GetAccessToken(string resourceId)
        {
            // Try the cache first:
            string accessToken = Office365Cache.GetAccessToken(resourceId).Value;

            if (accessToken != null)
            {
                return(accessToken);
            }

            // If there is no Access Token in the cache for this resource, check if there is a refresh token
            //    in the cache that can be used to get a new access token.
            accessToken = GetAccessTokenFromRefreshToken(resourceId);
            if (accessToken != null)
            {
                return(accessToken);
            }

            // If neither succeeded, return null to signal a need for an OAuth redirect.
            return(null);
        }
 /// <summary>
 /// If the item exists, returns the saved value; otherwise, returns null.
 /// </summary>
 internal static object GetFromCache(string name)
 {
     return(Office365Cache.GetFromCache(name));
 }
 internal static void SaveInCache(string name, object value)
 {
     Office365Cache.SaveInCache(name, value);
 }
        /// <summary>
        /// This method will be invoked as a call-back from an authentication service (e.g., https://login.windows.net/).
        /// It is not intended to be called directly, or to be called without first invoking the "GetAuthorizationUrl" method.
        /// On completion, the method will cache the refresh token and access tokens, and redirect to the URL
        ///     specified in the state cookie (created by the "GetAuthorizationUrl" method, with its unique ID
        ///     included in the "state" of this method).
        /// </summary>
        public ActionResult Index(string code, string error, string error_description, string state)
        {
            // NOTE: In production, OAuth must be done over a secure HTTPS connection.
            if (Request.Url.Scheme != "https" && !Request.Url.IsLoopback)
            {
                const string message = "Invalid URL. Please run the app over a secure HTTPS connection.";
                return(ShowErrorMessage(message, message + " URL: " + Request.Url.ToString()));
            }

            // Ensure that there is a state cookie on the the request.
            HttpCookie stateCookie = Request.Cookies[OAuthRequestStateCookiePrefix + state];

            if (stateCookie == null)
            {
                Office365Cache.RemoveAllFromCache();
                const string message     = "An authentication error has occurred. Please return to the previous page and try again.";
                string       errorDetail = "Missing OAuth state cookie." + " URL: " + Request.Url.ToString();
                return(ShowErrorMessage(message, errorDetail));
            }

            const string genericAuthenticationErrorMessage = "An authentication error has occurred.";

            // Retrieve the unique ID from the saved cookie, and compare it with the state parameter returned by
            //     the Azure Active Directory Authorization endpoint:
            Office365StateCookieInfo stateCookieInfo = JsonConvert.DeserializeObject <Office365StateCookieInfo>(stateCookie.Value);

            if (stateCookieInfo.UniqueId != state)
            {
                // State is mismatched, error
                Office365Cache.RemoveAllFromCache();
                string errorDetail = "OAuth state cookie mismatch." + " URL: " + Request.Url.ToString();
                return(ShowErrorMessage(genericAuthenticationErrorMessage, errorDetail));
            }

            // State check complete, clear the cookie:
            stateCookie.Expires = DateTime.Now.AddDays(-1);
            Response.Cookies.Set(stateCookie);

            // Handle error codes returned from the Authorization Server, if any:
            if (error != null)
            {
                Office365Cache.RemoveAllFromCache();
                return(ShowErrorMessage(genericAuthenticationErrorMessage,
                                        error + ": " + error_description + " URL: " + Request.Url.ToString()));
            }

            // If still here, redeem the authorization code for an access token:
            try
            {
                ClientCredential      credential  = new ClientCredential(AppPrincipalId, AppKey);
                string                authority   = string.Format(CultureInfo.InvariantCulture, OAuthUrl, "common");
                AuthenticationContext authContext = new AuthenticationContext(authority);
                AuthenticationResult  result      = authContext.AcquireTokenByAuthorizationCode(
                    code, new Uri(Request.Url.GetLeftPart(UriPartial.Path)), credential);

                // Cache the access token and refresh token
                Office365Cache.GetAccessToken(stateCookieInfo.ResourceId).Value = result.AccessToken;
                Office365Cache.GetRefreshToken().Value = result.RefreshToken;

                // Also save the Tenant ID and User ID
                SaveInCache("TenantId", result.TenantId);
                SaveInCache("UserId", result.UserInfo.UserId);

                return(Redirect(stateCookieInfo.RedirectTo));
            }
            catch (ActiveDirectoryAuthenticationException ex)
            {
                return(ShowErrorMessage(genericAuthenticationErrorMessage,
                                        "URL: " + Request.Url.ToString() + " Exception: " + ex.ToString()));
            }
        }
 public void RemoveFromCache()
 {
     Office365Cache.RemoveFromCache(_name);
 }