Search code examples
.net-coresharepointsharepoint-online.net-standardcsom

Access SharePoint API using CSOM from .NET Standard


I am trying to create a .NET core console app that can access a Office 365 SharePoint site. I'm use the recommended auth manager from the PnP Framework to try to establish a connection. I followed the MSDN instructions for Using modern authentication with CSOM for .NET Standard but I still get this unauthorized error:

System.Net.WebException: 'The remote server returned an error: (401) Unauthorized.'

Here is the code:

var authManager = new AuthenticationManager(clientId, tenantId: tenantId);

using var context = authManager.GetAccessTokenContext(siteUrl.ToString(), _ =>
{
    var app = ConfidentialClientApplicationBuilder.Create(clientId)
        .WithClientSecret(appSecret)
        .WithTenantId(tenantId)
        .Build();

    var resource = $"{siteUrl.Scheme}://{siteUrl.Authority}";
    var scopes = new[] { $"{resource}/.default" };
    var authResult = app.AcquireTokenForClient(scopes).ExecuteAsync().Result;
    return authResult.AccessToken;
});

var web = context.Web;
context.Load(web, w => w.Title);
context.ExecuteQuery();

I am able to successfully get an access token and I see it being passed during the request to execute the CSOM query but the server rejects it.

Here is the request and response from the server when CSOM attempts to use the access token to run the context.ExecuteQuery() line:

Request:

POST https://mysite.sharepoint.com/sites/FileStructureSandbox/_vti_bin/client.svc/ProcessQuery HTTP/1.1
Host: mysite.sharepoint.com
Authorization: Bearer eyJ0eX...... [REDACTED for StackOverflow post]
Connection: Keep-Alive
Accept-Encoding: gzip, deflate
Content-Type: text/xml
Content-Length: 606

<?xml version="1.0" encoding="UTF-8"?>
<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName=".NET Library">
   <Actions>
      <ObjectPath Id="2" ObjectPathId="1" />
      <ObjectPath Id="4" ObjectPathId="3" />
      <Query Id="5" ObjectPathId="3">
         <Query SelectAllProperties="false">
            <Properties>
               <Property Name="Title" ScalarProperty="true" />
            </Properties>
         </Query>
      </Query>
   </Actions>
   <ObjectPaths>
      <StaticProperty Id="1" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" />
      <Property Id="3" ParentId="1" Name="Web" />
   </ObjectPaths>
</Request>

Response:

Unsupported app only token.


Solution

  • It appears this has been addressed in Microsoft's Breaking Changes to SharePoint authentication.

    After many attempts, client secrets appear to be broken despite setting the DisableCustomAppAuthentication setting to false at the tenant level which according to the history behind it, should enable client secrets to work. It could be a cached delay of 24 hours like the link above mentioned.

    With the preferred route being certificates, I was able to connect successfully once I created a self-signed certificate and linked it to the Azure AD app by uploading my .cer file to Azure under Certificates & secrets > Certificates.

    Then the code that does the authentication loads the certificate:

    var app = ConfidentialClientApplicationBuilder.Create(clientId)
        .WithCertificate(new X509Certificate2("MyPrivateKey.pfx", "MY_PASSWORD"))
        .WithTenantId(tenantId)
        .Build();