0
votes

Hello Office / SharePoint Developers,

I am working on a project based on the Office Developer Patterns and Practices Sample where a console application accesses a WebAPI which then access SharePoint Online as the logged in user:  The sample is here: https://github.com/SharePoint/PnP/tree/master/Samples/AzureAD.WebAPI.SPOnline

Question: When I attempt to upload a file to the document library, I get an error 401 "The remote server returned an error: (401) Unauthorized".

The file read options such as listing the documents and querying for documents works fine.

The user credentials I supply are of a user that is the site collection admin, owner, and global admin on the tenant.

I get an access token from SharePoint online based on the token I get in the native client.

public string GetAccessToken(string accessToken)
{
    string clientID = _clientId;            
    string clientSecret = _clientSecret;
    var appCred = new ClientCredential(clientID, clientSecret);
    var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.windows.net/common");
    AuthenticationResult authResult = authContext.AcquireToken(new Uri(_spoUrl).GetLeftPart(UriPartial.Authority), appCred, new UserAssertion(accessToken));
    return authResult.AccessToken;
}

This is the CSOM that uploads the file.  I know it works as I can paste it into a console app and using (SharePointOnlineCredentails) it works fine.

string newToken = _tokenSvc.GetAccessToken(accessToken);
using (ClientContext cli = new ClientContext(_spoUrl))
{
    cli.ExecutingWebRequest += (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + newToken);
    cli.AuthenticationMode = ClientAuthenticationMode.Default;
    using (var fs = new FileStream(@"c:\test.txt", FileMode.Open))
    {
        var fi = new FileInfo("test.txt");
        var list = cli.Web.Lists.GetByTitle("documents");
        cli.Load(list.RootFolder);
        cli.ExecuteQuery();
        var fileUrl = String.Format("{0}/{1}", list.RootFolder.ServerRelativeUrl, fi.Name);
        Microsoft.SharePoint.Client.File.SaveBinaryDirect(cli, fileUrl, fs, true);
        Web web = cli.Web;
        Microsoft.SharePoint.Client.File newFile = web.GetFileByServerRelativeUrl(fileUrl);
        cli.Load(newFile);
        cli.ExecuteQuery();
        ListItem item = newFile.ListItemAllFields;
        item["CRUID"] = "CRU_1337";
        item.Update();
        cli.ExecuteQuery();
    }
}...

TLDR:  I get 401 on file upload.  Reads work.  I am using CSOM with an access token that is supposed to be a webAPI on behalf of the logged in user. I look forward to hearing your advice!

Chris

2

2 Answers

0
votes

I am not sure whether we could upload/download files from SP using access tokens with CSOM now , see discussion here two years ago . But we could use sharepoint online rest api to upload files to sharepoint online , i tried below code and it works fine in the code sample AzureAD.WebAPI.SPOnline :

            string sharePointUrl = ConfigurationManager.AppSettings["SharePointURL"];
            string newToken = GetSharePointAccessToken(sharePointUrl, this.Request.Headers.Authorization.Parameter);
            using (ClientContext cli = new ClientContext(sharePointUrl))
            {


                cli.AuthenticationMode = ClientAuthenticationMode.Default;
                cli.ExecutingWebRequest += (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + newToken);
                cli.AuthenticationMode = ClientAuthenticationMode.Default;


                byte[] bytefile = System.IO.File.ReadAllBytes(@"e:\log.txt");

                HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create("https://xxx.sharepoint.com/sites/xxx/" + "/_api/web/GetFolderByServerRelativeUrl('Shared%20Documents')/Files/add(url='log.txt',overwrite=true)");
                endpointRequest.Method = "POST";
                endpointRequest.Headers.Add("binaryStringRequestBody", "true");
                endpointRequest.Headers.Add("Authorization", "Bearer " + newToken);
                endpointRequest.GetRequestStream().Write(bytefile, 0, bytefile.Length);

                HttpWebResponse endpointresponse = (HttpWebResponse)endpointRequest.GetResponse();

            }
0
votes

The code below is ended up being the solution to my question:

/* Beginning CSOM Magic */
        using (ClientContext cli = new ClientContext(_spoUrl))
        {
            /* Adding authorization header  */
            cli.ExecutingWebRequest += (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + newToken);
            cli.AuthenticationMode = ClientAuthenticationMode.Default;

            //Get Document List
            List documentsList = cli.Web.Lists.GetByTitle(_libraryName);

            var fileCreationInformation = new FileCreationInformation();

            //Assign to content byte[] i.e. documentStream
            var data = System.IO.File.ReadAllBytes(@"c:\test.txt");
            fileCreationInformation.Content = data;

            //Allow owerwrite of document
            fileCreationInformation.Overwrite = true;

            //var siteURL = _spoUrl;
            var documentListURL = "shared documents";
            //var documentName = "/test.txt";

            //Upload URL
            fileCreationInformation.Url = string.Concat(_spoUrl,"/",documentListURL,"/",documentName);
            Microsoft.SharePoint.Client.File uploadFile = documentsList.RootFolder.Files.Add(
                fileCreationInformation);

            //Update the metadata for a field having name "DocType"
            uploadFile.ListItemAllFields["CRUID"] = cruId;

            uploadFile.ListItemAllFields.Update();
            cli.ExecuteQuery();
        }