Search code examples
c#sharepoint.net-coresharepoint-onlinecsom

Unable to authenticate SharePoint REST or CSOM calls from .NET Core


I am having problems accessing SharePoint using both CSOM and the REST service from a .NET Core console application.

First I created a console application that targets .NET Framework 4.6.1, installed the Microsoft.SharePointOnline.CSOM nuget package, and added two sample methods to connect to SharePoint using hard-coded user credentials. This works.

public static void CsomCall()
{
    var password = "password";
    var securePassword = new SecureString();
    foreach (var c in password.ToCharArray()) securePassword.AppendChar(c);

    using (ClientContext context = new ClientContext("https://siteurl"))
    {
        context.Credentials = new SharePointOnlineCredentials("user@domain", securePassword);
        Web web = context.Web;
        context.Load(web);
        context.ExecuteQuery();
        Console.WriteLine(web.Title);
    }
}
private static void RestCall()
{
    var password = "password";
    var securePassword = new SecureString();
    foreach (var c in password.ToCharArray()) securePassword.AppendChar(c);

    var credentials = new SharePointOnlineCredentials("user@domain", securePassword);

    using (WebClient client = new WebClient())
    {
        client.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
        client.Credentials = credentials;
        client.Headers.Add(HttpRequestHeader.ContentType, "application/json;odata=verbose");
        client.Headers.Add(HttpRequestHeader.Accept, "application/json;odata=verbose");
        var content = client.DownloadString("https://siteurl/_api/web/lists");
        Console.WriteLine(content);
    }
}

Then I created a .NET Core console application and copied across the methods above. I understand that SharePoint CSOM is not yet supported in .NET Core, so I used the workaround suggested here and manually referenced Microsoft.SharePoint.Client.Portable.dll, Microsoft.SharePoint.Client.Runtime.Portable.dll and Microsoft.SharePoint.Client.Runtime.Windows.dll.

I had to make a couple of changes to compile the code:

  • .ExecuteQueryAsync() instead of .ExecuteQuery()

  • var credentials = new SharePointOnlineCredentials("user@domain", password); Note, password is a plain string in Microsoft.SharePoint.Client.Runtime.Portable.

When running CsomCall it fails when accessing web.Title:

Microsoft.SharePoint.Client.PropertyOrFieldNotInitializedException: 'The property or field 'Title' has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.'

When running RestCall it fails with an error on client.DownloadString:

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

Is it possible to set up SharePointOnlineCredentials to work with .NET Core? Searches on stack overflow indicate this should be possible, but I just can't seem to make it work.

Ultimately we would like to build a Web API service using ASP.NET Core to generate documents for internal users (reading document templates from SharePoint, generating a new document and saving back to SharePoint). Using either CSOM or REST, and running on Windows OS for now.


Solution

  • The REST call is wrong. You have to get a token using the credentials. Also, the WebClient is mostly deprecated, use the HttpClient class instead.

    Look at this example:

    public const string BaseUri = "https://example.sharepoint.com";
    private static HttpClient _client;
    
    public static void Initialize()
    {
        SharePointOnlineCredentials currentCredentials = GetCredentialsHere();
    
        var handler = new HttpClientHandler
        {
            Credentials = currentCredentials
        };
    
        _client = new HttpClient(handler);
    
        // you are missing this line
        handler.CookieContainer.SetCookies(BaseUri, currentCredentials.GetAuthenticationCookie(BaseUri));
        _client.BaseAddress = BaseUri;
        _client.DefaultRequestHeaders.Clear();
        _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        _client.MaxResponseContentBufferSize = 2147483647;
    }