Search code examples
c#azure-active-directorymicrosoft-graph-apiaccess-tokenabp-framework

Error "400: Bad Request" message "/me request is only valid with delegated authentication flow."


I'm trying to use the Graph API to upload data to an Excel table using ABP.io. However, I'm getting a "400: Bad Request" error with the message "/me request is only valid with delegated authentication flow."

Here's the relevant code snippet:

using Azure.Core;
using Azure.Identity;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;

namespace ExcelApi.Excel;

public class ExcelAppService : ApplicationService
{
    public async Task<string> SaveData(UserInfo data)
    {
        string endpoint = "https://graph.microsoft.com/v1.0/me/drive/items/f1b665ee-b57f-4fe8-aa1d-c8c94d5eb7ff/workbook/tables/Table1/rows/add";
        using (var client = new HttpClient())
        {
            using (var request = new HttpRequestMessage(HttpMethod.Post, endpoint))
            {
                var scopes = new[] { "https://graph.microsoft.com/.default" };
                // Define the scopes that you want to access.

                // Create a new instance of ClientSecretCredential using the client ID, client secret, and tenant ID.
                var credential = new ClientSecretCredential(Client.TenantId, Client.Id, Client.Secret);

                // Get an access token for the specified scopes.
                var accessToken = await credential.GetTokenAsync(new TokenRequestContext(scopes));

                // Populate UserInfoRequest object
                string[] userInfo = { data.Name, data.Address };
                string[][] userInfoArray = { userInfo };
                UserInfoRequest userInfoRequest = new UserInfoRequest();
                userInfoRequest.index = null;
                userInfoRequest.values = userInfoArray;

                // Serialize the information in the UserInfoRequest object
                string jsonBody = JsonConvert.SerializeObject(userInfoRequest);
                request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token);
                request.Content = new StringContent(jsonBody, Encoding.UTF8, "application/json");

                using (var response = await client.SendAsync(request))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        return "Graph upload to Excel succeeded."; // Replace Resource.Graph_UploadToExcel_Success_Result with the desired string
                    }
                    return accessToken.Token;
                    //return response.ReasonPhrase;
                }
            }
        }
    }
}

I've checked that the client ID, client secret, and tenant ID are correct, I get the error message mentioned above.

Can anyone help me understand what I'm doing wrong and how to fix this error?


Solution

  • /me is essentially an alias for /users/<currently-signed-in-user-id>. In order for Graph to figure out the current user ID, there needs to be a user ID in the access token it receives. The flow you are using to get the token (Client credentials flow) does not include user information in the token.

    You have two options:

    1. Continue using client credentials flow + application permissions. Use /users/<user-id>/... instead of /me/....
    2. Switch to an authentication flow that includes a user. In your case you could use the InteractiveBrowserCredential.