Getting 403 "Forbidden" error sending a file to Sharepoint using Microsoft Graph in c#.

Calingo, Rolando V 0 Reputation points
2024-04-15T13:43:03.79+00:00

How can I resolve the issue of receiving HTTP error 403 "Forbidden" when trying to upload and add an item to a Sharepoint list using Microsoft Graph and C#? I have retrieved a token using a client ID and secret from Azure/Entra, and have tried to use a certificate both ways with the same result. I can create a list without issue, but continue to receive the error when trying to upload a file to a newly created list. I have changed the Sharepoint permission from write to full control, but the problem persists.

Here is the code I'm using both getting token with App_id, secret and the other with certificate.

public static async Task<string> GetGraphToken(string clientId, string clientSecret, string tenantId)
        {
            string retValue = "";
                        
            string authority = "https://login.microsoftonline.com/" + tenantId;
            var app = ConfidentialClientApplicationBuilder
                .Create(clientId)
                .WithClientSecret(clientSecret)
                .WithAuthority(new Uri(authority))
                .Build();
            var scopes = new[] { "https://graph.microsoft.com/.default" };
            try
            {
                var result = await app.AcquireTokenForClient(scopes)
                    .ExecuteAsync();
                Console.WriteLine($"Access token: {result.AccessToken}");
                retValue = result.AccessToken;
            }
            catch (MsalServiceException ex)
            {
                Console.WriteLine($"Error acquiring token: {ex.Message}");
            }
            return retValue;
        }

public static async Task<string> GetGraphTokenCertificate(string clientId, string clientSecret, string tenantId, string certificateThumbprint)
        {
            string retValue = "";
            string authority = $"https://login.microsoftonline.com/{tenantId}";
            var cert = GetCertificateByThumbprint(certificateThumbprint);
            var app = ConfidentialClientApplicationBuilder.Create(clientId)
                .WithAuthority(authority)
                .WithCertificate(cert)
                .Build();
            string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
            var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
            retValue = result.AccessToken;
            // Now you can use the access token to authenticate with SharePoint using CSOM
            return retValue;
        }
static X509Certificate2 GetCertificateByThumbprint(string thumbprint)
        {
            X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false);
            store.Close();
            return certificates.Count > 0 ? certificates[0] : null;
        }


public static async Task<string> ListCreate(string accessToken, string siteId, string folderId, string listName)
        {
            string retValue = "";
            
            // Microsoft Graph endpoint to create a list
            string graphUrl = "https://graph.microsoft.com/v1.0/sites/{site-id}/lists";
            // Folder relative URL where the list will be created
            //string folderUrl = "/sites/{site-name}/lists";
            // Define columns
            var columns = new List<ColumnDefinition>
            {
                new ColumnDefinition
                {
                    Name = "Title",
                    Text = new TextColumn()
                },
                new ColumnDefinition
                {
                    Name = "Modified",
                    DateTime = new DateTimeColumn()
                },
                new ColumnDefinition
                {
                    Name = "Created",
                    DateTime = new DateTimeColumn()
                },
                new ColumnDefinition
                {
                    Name = "BatchName",
                    Text = new TextColumn()
                },
                new ColumnDefinition
                {
                    Name = "FileName",
                    Text = new TextColumn()
                },
                new ColumnDefinition
                {
                    Name = "ScanDate",
                    DateTime = new DateTimeColumn()
                },
                new ColumnDefinition
                {
                    Name = "Processed By",
                    PersonOrGroup = new PersonOrGroupColumn()
                },
                new ColumnDefinition
                {
                    Name = "Created By",
                    PersonOrGroup = new PersonOrGroupColumn()
                },
                new ColumnDefinition
                {
                    Name = "Modified By",
                    PersonOrGroup = new PersonOrGroupColumn()
                },
                // Attachment column
                //new ColumnDefinition
                //{
                //    Name = "Attachments",
                //    Attachments = new AttachmentColumn()
                //}
                // Add more columns as needed
            };
    
            // Create a JSON payload for creating the list with columns
            string jsonPayload = JsonConvert.SerializeObject(new
            {
                displayName = listName,
                list = new
                {
                    template = "genericList",
                    AllowAttachments = true, // Enable attachments
                    columns = columns
                },
                
                parentReference = new
                 {
                     driveId = folderId // specify the parent folder ID
                 }
                
            });
            // Prepare and send HTTP request to Microsoft Graph API
            using (var httpClient = new HttpClient())
            {
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
                var response = await httpClient.PostAsync(graphUrl.Replace("{site-id}", siteId).Replace("{site-name}", siteId), content);
                if (response.IsSuccessStatusCode)
                {
                    Console.WriteLine("List with columns, attachment, and date columns created successfully.");
                }
                else
                {
                    Console.WriteLine($"Failed to create list with columns, attachment, and date columns. StatusCode: {response.StatusCode}");
                }
            }
            return retValue;
        }


public static async Task<string> SendToSharePoint(string token, string siteId, string listId, string title, string batchName, string fileName, string scanDate, string attachmentin64base)
        {
            string retValue = "";
            if (!string.IsNullOrEmpty(token))
            {
                var item = new
                {
                    fields = new
                    {
                        Title = title,
                        Created = DateTime.Now.ToString("MM/dd/yyyy h:mm tt"),
                        BatchName = batchName,
                        FileName = fileName,
                        ScanDate = scanDate,
                    },
                    /*
                    attachments = new[]
                    {
                        new
                        {
                            fileName = fileName,
                            contentBytes = attachmentin64base
                        }
                    }
                    */
                };
            
                await AddListItem(token, siteId, listId, item);
            }
            return retValue;
        }


public static async Task<string> AddListItem(string accessToken, string siteId, string listId, object item)
        {
            string retValue = "";
            string addItemUrl = $"https://graph.microsoft.com/v1.0/sites/{siteId}/lists/{listId}/items";
            using (var httpClient = new HttpClient())
            {
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                var itemContent = new StringContent(JsonConvert.SerializeObject(item), Encoding.UTF8, "application/json");
                var response = await httpClient.PostAsync(addItemUrl, itemContent);
                if (response.IsSuccessStatusCode)
                {
                    Console.WriteLine("Item added successfully.");
                }
                else
                {
                    Console.WriteLine($"Failed to add item: {response.ReasonPhrase}");
                }
            }
            return retValue;
        }

Microsoft Graph
Microsoft Graph
A Microsoft programmability model that exposes REST APIs and client libraries to access data on Microsoft 365 services.
10,618 questions
SharePoint
SharePoint
A group of Microsoft Products and technologies used for sharing and managing content, knowledge, and applications.
9,662 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. RaytheonXie_MSFT 31,376 Reputation points Microsoft Vendor
    2024-04-16T06:38:22.72+00:00

    Hi @Calingo, Rolando V,

    You can use GraphServiceClient without user interaction using a client id and a client secret. First, create a class called GraphAuthProvider:

        public class GraphAuthProvider
    {
        public async Task<GraphServiceClient> AuthenticateViaAppIdAndSecret(
            string tenantId,
            string clientId, 
            string clientSecret)
        {
            var scopes = new string[] { "https://graph.microsoft.com/.default" };
    
            // Configure the MSAL client as a confidential client
            var confidentialClient = ConfidentialClientApplicationBuilder
                .Create(clientId)
                .WithAuthority($"https://login.microsoftonline.com/{tenantId}/v2.0")
                .WithClientSecret(clientSecret)
                .Build();
    
            // Build the Microsoft Graph client. As the authentication provider, set an async lambda
            // which uses the MSAL client to obtain an app-only access token to Microsoft Graph,
            // and inserts this access token in the Authorization header of each API request. 
            GraphServiceClient graphServiceClient =
                new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
                {
    
                    // Retrieve an access token for Microsoft Graph (gets a fresh token if needed).
                    var authResult = await confidentialClient
                        .AcquireTokenForClient(scopes)
                        .ExecuteAsync();
    
                    // Add the access token in the Authorization header of the API request.
                    requestMessage.Headers.Authorization =
                        new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
                })
            );
    
            return graphServiceClient;
        }
    }
    
    
    

    You can then create authenticated GraphServiceClients and use them to upload files, for example to SharePoint:

            GraphServiceClient _graphServiceClient = await _graphAuthProvider.AuthenticateViaAppIdAndSecret(
                tenantId,
                clientId,
                appSecret);
    
    
            using (Stream fileStream = new FileStream(
                fileLocation,
                FileMode.Open,
                FileAccess.Read))
            {
                resultDriveItem = await _graphServiceClient.Sites[sites[0]]
                        .Drives[driveId].Root.ItemWithPath(fileName).Content.Request().PutAsync<DriveItem>(fileStream);
    
           }
    

    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.