예제 #1
0
        /// <summary>
        /// Gets all the charts in the specified Excel workbook and presents them in a view.
        /// </summary>
        /// <param name="id">The internal ID of the workbook.</param>
        /// <returns>The view with the list of charts.</returns>
        public async Task <ActionResult> Index(string id)
        {
            // Get access token from the local database
            var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

            var sheetsUrl = GraphApiHelper.GetSheetsWithChartsUrl(id, "&$select=name,id");
            var sheets    = await ODataHelper.GetItems <ExcelSheet>(sheetsUrl, token.AccessToken);

            // Merge the charts from each worksheet into a single list
            List <Chart> allChartsInWorkbook = new List <Chart>();

            foreach (var sheet in sheets)
            {
                var chartsFromSheet = sheet.Charts;

                // The GetChartImage method requires a clean charts URL, that is, no $select option.
                string cleanFullChartsUrl = GraphApiHelper.GetChartsUrl(id, sheet.Id, null);

                foreach (var chart in chartsFromSheet)
                {
                    // string singleChartImageUrl = GraphApiHelper.GetSingleChartImageUrl(cleanFullChartsUrl, chart.Id);
                    chart.ImageAsBase64String = await GraphApiHelper.GetChartImage(cleanFullChartsUrl, chart.Id, token.AccessToken);
                }

                allChartsInWorkbook = allChartsInWorkbook.Concat(chartsFromSheet).ToList();
            }
            return(View(allChartsInWorkbook));
        }
예제 #2
0
        // GET api/values
        public async Task <IEnumerable <string> > Get()
        {
            // OWIN middleware validated the audience and issuer, but the scope must also be validated; must contain "access_as_user".
            string[] addinScopes = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value.Split(' ');
            if (addinScopes.Contains("access_as_user"))
            {
                // Get the raw token that the add-in page received from the Office host.
                var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext
                                       as BootstrapContext;
                UserAssertion userAssertion = new UserAssertion(bootstrapContext.Token);

                // Get the access token for MS Graph.
                ClientCredential clientCred       = new ClientCredential(ConfigurationManager.AppSettings["ida:Password"]);
                ConfidentialClientApplication cca =
                    new ConfidentialClientApplication(ConfigurationManager.AppSettings["ida:ClientID"],
                                                      "https://localhost:44355", clientCred, null, null);
                string[]             graphScopes = { "Files.Read.All" };
                AuthenticationResult result      = null;
                try
                {
                    // The AcquireTokenOnBehalfOfAsync method will first look in the MSAL in memory cache for a
                    // matching access token. Only if there isn't one, does it initiate the "on behalf of" flow
                    // with the Azure AD V2 endpoint.
                    result = await cca.AcquireTokenOnBehalfOfAsync(graphScopes, userAssertion, "https://login.microsoftonline.com/common/oauth2/v2.0");
                }
                catch (MsalUiRequiredException e)
                {
                    // If multi-factor authentication is required by the MS Graph resource an
                    // the user has not yet provided it, AAD will throw an exception containing a
                    // Claims property.
                    if (String.IsNullOrEmpty(e.Claims))
                    {
                        throw e;
                    }
                    else
                    {
                        // The Claims property value must be passed to the client which will pass it
                        // to the Office host, which will then include it in a request for a new token.
                        // AAD will prompt the user for all required forms of authentication.
                        throw new HttpException(e.Claims);
                    }
                }

                // Get the names of files and folders in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
                var fullOneDriveItemsUrl = GraphApiHelper.GetOneDriveItemNamesUrl("?$select=name&$top=3");
                var getFilesResult       = await ODataHelper.GetItems <OneDriveItem>(fullOneDriveItemsUrl, result.AccessToken);

                // The returned JSON includes OData metadata and eTags that the add-in does not use.
                // Return to the client-side only the filenames.
                List <string> itemNames = new List <string>();
                foreach (OneDriveItem item in getFilesResult)
                {
                    itemNames.Add(item.Name);
                }
                return(itemNames);
            }
            return(new string[] { "Error", "Microsoft Office does not have permission to get Microsoft Graph data on behalf of the current user." });
        }
        /// <summary>
        /// Recursively searches OneDrive for Business.
        /// </summary>
        /// <returns>A view listing the workbooks in OneDrive for Business.</returns>
        public async Task <ActionResult> OneDriveFiles()
        {
            // Get access token
            var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

            // Get all the Excel files in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
            var fullWorkbooksSearchUrl = GraphApiHelper.GetWorkbookSearchUrl("?$select=name,id");
            var getFilesResult         = await ODataHelper.GetItems <ExcelWorkbook>(fullWorkbooksSearchUrl, token.AccessToken);

            return(View(getFilesResult));
        }
예제 #4
0
        /// <summary>
        /// Recursively searches OneDrive for Business.
        /// </summary>
        /// <returns>The names of the first three workbooks in OneDrive for Business.</returns>
        public async Task <JsonResult> OneDriveFiles()
        {
            // Get access token
            var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

            // Get all the Excel files in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
            var fullWorkbooksSearchUrl = GraphApiHelper.GetWorkbookSearchUrl("?$select=name,id&top=3");
            var filesResult            = await ODataHelper.GetItems <ExcelWorkbook>(fullWorkbooksSearchUrl, token.AccessToken);

            List <string> fileNames = new List <string>();

            foreach (ExcelWorkbook workbook in filesResult)
            {
                fileNames.Add(workbook.Name);
            }
            return(Json(fileNames, JsonRequestBehavior.AllowGet));
        }
        // GET api/values
        public async Task <HttpResponseMessage> Get()
        {
            // OWIN middleware validated the audience and issuer, but the scope must also be validated; must contain "access_as_user".
            string[] addinScopes = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value.Split(' ');
            if (addinScopes.Contains("access_as_user"))
            {
                // Get the raw token that the add-in page received from the Office host.
                var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext
                                       as BootstrapContext;
                UserAssertion userAssertion = new UserAssertion(bootstrapContext.Token);

                // Get the access token for MS Graph.
                ClientCredential clientCred       = new ClientCredential(ConfigurationManager.AppSettings["ida:Password"]);
                ConfidentialClientApplication cca =
                    new ConfidentialClientApplication(ConfigurationManager.AppSettings["ida:ClientID"],
                                                      "https://localhost:44355", clientCred, null, null);
                string[]             graphScopes = { "Files.Read.All" };
                AuthenticationResult result      = null;
                try
                {
                    // The AcquireTokenOnBehalfOfAsync method will first look in the MSAL in memory cache for a
                    // matching access token. Only if there isn't one, does it initiate the "on behalf of" flow
                    // with the Azure AD V2 endpoint.
                    result = await cca.AcquireTokenOnBehalfOfAsync(graphScopes, userAssertion, "https://login.microsoftonline.com/common/oauth2/v2.0");
                }
                catch (MsalServiceException e)
                {
                    // If multi-factor authentication is required by the MS Graph resource and the user
                    // has not yet provided it, AAD will return "400 Bad Request" with error AADSTS50076
                    // and a Claims property. MSAL throws a MsalUiRequiredException (which inherits
                    // from MsalServiceException) with this information. The Claims property value must
                    // be passed to the client which should pass it to the Office host, which then
                    // includes it in a request for a new token. AAD will prompt the user for all
                    // required forms of authentication.
                    if (e.Message.StartsWith("AADSTS50076"))
                    {
                        // The APIs that create HTTP Responses from exceptions don't know about the
                        // Claims property, so they don't include it. We have to manually create a message
                        // that includes it. A custom Message property, however, blocks the creation of an
                        // ExceptionMessage property, so the only way to get the error AADSTS50076 to the
                        // client is to add it to the custom Message. JavaScript in the client will need
                        // to discover if a response has a Message or ExceptionMessage, so it knows which
                        // to read.
                        string responseMessage = String.Format("{{\"AADError\":\"AADSTS50076\",\"Claims\":{0}}}", e.Claims);
                        return(SendErrorToClient(HttpStatusCode.Forbidden, null, responseMessage));
                    }

                    // If the call to AAD contained at least one scope (permission) for which neither
                    // the user nor a tenant administrator has consented (or consent was revoked.
                    // AAD will return "400 Bad Request" with error AADSTS65001. MSAL throws a
                    // MsalUiRequiredException with this information. The client should re-call
                    // getAccessTokenAsync with the option { forceConsent: true }.
                    if ((e.Message.StartsWith("AADSTS65001"))

                        // If the call to AAD contained at least one scope that AAD does not recognize,
                        // AAD returns "400 Bad Request" with error AADSTS70011. MSAL throws a
                        // MsalUiRequiredException (which inherits from MsalServiceException) with this
                        // information. The client should inform the user.
                        || (e.Message.StartsWith("AADSTS70011: The provided value for the input parameter 'scope' is not valid.")))
                    {
                        return(SendErrorToClient(HttpStatusCode.Forbidden, e, null));
                    }
                    else
                    {
                        // Rethrowing the MsalServiceException will not relay the original
                        // "400 Bad Request" exception to the client. Instead a "500 Server Error"
                        // is sent.
                        throw e;
                    }
                }

                // Get the names of files and folders in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
                // Note that the parameter is hardcoded. If you reuse this code in a production add-in and any part of the query parameter comes
                // from user input, be sure that it is sanitized so that it cannot be used in a Response header injection attack.
                var fullOneDriveItemsUrl = GraphApiHelper.GetOneDriveItemNamesUrl("?$select=name&$top=3");

                IEnumerable <OneDriveItem> filesResult;
                try
                {
                    filesResult = await ODataHelper.GetItems <OneDriveItem>(fullOneDriveItemsUrl, result.AccessToken);
                }

                // If the token is invalid, MS Graph sends a "401 Unauthorized" error with the code
                // "InvalidAuthenticationToken". ASP.NET then throws a RuntimeBinderException. This
                // is also what happens when the token is expired, although MSAL should prevent that
                // from ever happening. In either case, the client should start the process over by
                // re-calling getAccessTokenAsync.
                catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e)
                {
                    return(SendErrorToClient(HttpStatusCode.Unauthorized, e, null));
                }

                // The returned JSON includes OData metadata and eTags that the add-in does not use.
                // Return to the client-side only the filenames.
                List <string> itemNames = new List <string>();
                foreach (OneDriveItem item in filesResult)
                {
                    itemNames.Add(item.Name);
                }

                var requestMessage = new HttpRequestMessage();
                requestMessage.SetConfiguration(new HttpConfiguration());
                var response = requestMessage.CreateResponse <List <string> >(HttpStatusCode.OK, itemNames);
                return(response);
            }
            // The token from the client does not have "access_as_user" permission.
            return(SendErrorToClient(HttpStatusCode.Unauthorized, null, "Missing access_as_user."));
        }
예제 #6
0
        // GET api/values
        public async Task <HttpResponseMessage> Get()
        {
            Dictionary <string, string> errorObj = new Dictionary <string, string>();

            // OWIN middleware validated the audience and issuer, but the scope must also be validated; must contain "access_as_user".
            string[] addinScopes = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value.Split(' ');
            if (addinScopes.Contains("access_as_user"))
            {
                // Get the raw token that the add-in page received from the Office host.
                var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext
                                       as BootstrapContext;

                // Get the access token for MS Graph.
                string[] graphScopes = { "Files.Read.All" };

                GraphToken result = null;
                try
                {
                    // The AcquireTokenOnBehalfOfAsync method will initiate the "on behalf of" flow
                    // with the Azure AD V2 endpoint.
                    result = await GraphTokenHelper.AcquireTokenOnBehalfOfAsync(bootstrapContext.Token, graphScopes);
                }
                catch (GraphTokenException e)
                {
                    // Even we have the access token from Office, it's possible the Azure AD fails the request.
                    // Potential reasons could be: 1) Require MFA, 2) Missing consent
                    // Why missing consent could happen?
                    // To get an access token from Office client, user only needs to grant the "access_as_user" scope.
                    // It doesnt mean user has granted all the required scopes.
                    // If the required scopes have updated (for example, the developer change the scopes in http://apps.dev.microsoft.com/,
                    // user has to consent again for those new scopes.
                    // Therefore, the server should send the error to the client and let the client to handle it.
                    errorObj["claims"]    = e.Claims;
                    errorObj["message"]   = e.Message;
                    errorObj["errorCode"] = e.ErrorCode;
                    errorObj["suberror"]  = e.SubError;
                    return(SendErrorToClient(HttpStatusCode.Unauthorized, errorObj));
                }
                catch (Exception e)
                {
                    errorObj["errorCode"] = "unknown_error";
                    errorObj["message"]   = e.Message;
                    return(SendErrorToClient(HttpStatusCode.InternalServerError, errorObj));
                }

                // Get the names of files and folders in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
                var fullOneDriveItemsUrl = GraphApiHelper.GetOneDriveItemNamesUrl("?$select=name&$top=3");

                IEnumerable <OneDriveItem> filesResult;
                try
                {
                    filesResult = await ODataHelper.GetItems <OneDriveItem>(fullOneDriveItemsUrl, result.AccessToken);
                }

                // If the token is invalid, MS Graph sends a "401 Unauthorized" error with the code
                // "InvalidAuthenticationToken". ASP.NET then throws a RuntimeBinderException. This
                // is also what happens when the token is expired, although MSAL should prevent that
                // from ever happening. In either case, the client should start the process over by
                // re-calling getAccessTokenAsync.
                catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e)
                {
                    errorObj["errorCode"] = "invalid_graph_token";
                    errorObj["message"]   = e.Message;
                    return(SendErrorToClient(HttpStatusCode.Unauthorized, errorObj));
                }

                // The returned JSON includes OData metadata and eTags that the add-in does not use.
                // Return to the client-side only the filenames.
                List <string> itemNames = new List <string>();
                foreach (OneDriveItem item in filesResult)
                {
                    itemNames.Add(item.Name);
                }

                var requestMessage = new HttpRequestMessage();
                requestMessage.SetConfiguration(new HttpConfiguration());
                var response = requestMessage.CreateResponse <List <string> >(HttpStatusCode.OK, itemNames);
                return(response);
            }
            else
            {
                // The token from the client does not have "access_as_user" permission.
                errorObj["errorCode"] = "invalid_access_token";
                errorObj["message"]   = "Missing access_as_user. Microsoft Office does not have permission to get Microsoft Graph data on behalf of the current user.";
                return(SendErrorToClient(HttpStatusCode.Unauthorized, errorObj));
            }
        }