/// <summary>
        /// Returns a URL through which a user can be authorized to access Office 365 APIs.
        /// After the authorization is complete, the user will be redirected back to the URL
        ///     defined by the redirectTo parameter. This can be the same URL as the caller's URL
        ///     (e.g., Request.Url), or it can contain additional query-string parameters
        ///     (e.g., to restore state).
        /// </summary>
        internal static string GetAuthorizationUrl(string resourceId, Uri redirectTo)
        {
            HttpContext context = System.Web.HttpContext.Current;

            // To prevent Cross-Site Request Forgery attacks (http://tools.ietf.org/html/rfc6749 section 4.2.1),
            //     it is important to send a randomly-generated value as a state parameter.
            // This state parameter is saved in a cookie, so it can later be compared with the state
            //     parameter that we receive from the Authorization Server along with the Authorization Code.
            // The state cookie will also capture information about the resource ID and redirect-to URL,
            //     for use in the Index method (after the login page redirects back to this controller).
            Office365StateCookieInfo stateCookieInfo = new Office365StateCookieInfo
            {
                UniqueId   = Guid.NewGuid().ToString(),
                ResourceId = resourceId,
                RedirectTo = redirectTo.ToString()
            };
            HttpCookie stateCookie = new HttpCookie(OAuthRequestStateCookiePrefix + stateCookieInfo.UniqueId)
            {
                HttpOnly = true,
                Secure   = !context.Request.Url.IsLoopback,
                Value    = JsonConvert.SerializeObject(stateCookieInfo),
                Expires  = DateTime.Now.AddMinutes(10)
            };

            context.Response.Cookies.Add(stateCookie);

            // Create an OAuth request URL. To avoid introducing auth-related complexity into
            //     individual controllers, the Office365CommonController will handle the entire auth flow,
            //     and only redirect to the original caller on completion.
            Uri redirectToThisController = new Uri(context.Request.Url, "/Office365Common");

            return(String.Format(CultureInfo.InvariantCulture,
                                 AuthorizeUrl,
                                 Uri.EscapeDataString(AppPrincipalId),
                                 Uri.EscapeDataString(resourceId),
                                 Uri.EscapeDataString(redirectToThisController.ToString()),
                                 Uri.EscapeDataString(stateCookieInfo.UniqueId)));
        }
        /// <summary>
        /// Returns a URL through which a user can be authorized to access Office 365 APIs.
        /// After the authorization is complete, the user will be redirected back to the URL 
        ///     defined by the redirectTo parameter. This can be the same URL as the caller's URL
        ///     (e.g., Request.Url), or it can contain additional query-string parameters
        ///     (e.g., to restore state).
        /// </summary>
        internal static string GetAuthorizationUrl(string resourceId, Uri redirectTo)
        {
            HttpContext context = System.Web.HttpContext.Current;

            // To prevent Cross-Site Request Forgery attacks (http://tools.ietf.org/html/rfc6749 section 4.2.1),
            //     it is important to send a randomly-generated value as a state parameter.
            // This state parameter is saved in a cookie, so it can later be compared with the state
            //     parameter that we receive from the Authorization Server along with the Authorization Code.
            // The state cookie will also capture information about the resource ID and redirect-to URL,
            //     for use in the Index method (after the login page redirects back to this controller).
            Office365StateCookieInfo stateCookieInfo = new Office365StateCookieInfo
            {
                UniqueId = Guid.NewGuid().ToString(),
                ResourceId = resourceId,
                RedirectTo = redirectTo.ToString()
            };
            HttpCookie stateCookie = new HttpCookie(OAuthRequestStateCookiePrefix + stateCookieInfo.UniqueId)
            {
                HttpOnly = true,
                Secure = !context.Request.Url.IsLoopback,
                Value = JsonConvert.SerializeObject(stateCookieInfo),
                Expires = DateTime.Now.AddMinutes(10)
            };
            context.Response.Cookies.Add(stateCookie);

            // Create an OAuth request URL. To avoid introducing auth-related complexity into 
            //     individual controllers, the Office365CommonController will handle the entire auth flow,
            //     and only redirect to the original caller on completion.
            Uri redirectToThisController = new Uri(context.Request.Url, "/Office365Common");

            return String.Format(CultureInfo.InvariantCulture,
                AuthorizeUrl,
                Uri.EscapeDataString(AppPrincipalId),
                Uri.EscapeDataString(resourceId),
                Uri.EscapeDataString(redirectToThisController.ToString()),
                Uri.EscapeDataString(stateCookieInfo.UniqueId));
        }
        /// <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()));
            }
        }