5

I feel like I'm missing something here. I'm trying to login to a SPO tenant via a C# Console app, but I'm getting the error:

Cannot contact web site 'https://xxx.sharepoint.com/' or the web site does not support SharePoint Online credentials. The response status code is 'Unauthorized'.

I know the account works, as I'm able to login with the account directly from browsers. In the end, I'd like to use the CSOM to do CRUD operations on SP Lists, Term Stores and Document Libraries, but CSOM is not a hard requirement (and I admittedly don't know if REST APIs can do the whole job).

I've seen that changing the LegacyAuthProtocolsEnabled value to True can help with this, but our security folks won't allow me to enable that feature.

The code is pretty vanilla:

SecureString passWord = getPassword();
using (var context = new ClientContext(URI))
{
    context.Credentials = new SharePointOnlineCredentials(userName, passWord);//new NetworkCredential(userName, passWord);//
    context.Load(context.Web, web => web.Title);
    context.ExecuteQuery(); //Error happens here
    Console.WriteLine("Your site title is: " + context.Web.Title);
}

As you can see, I've also tried passing the NetworkCredentials object, which also doesn't work (I also get a 401 response).

As a further note, I've looked into App-only identities, but I don't believe I can use those due to their restrictions on managing taxonomy (ie. managed metadata within Term Stores) and managing files (though this may be a restriction only on using CSOM, it's not clear to me from this page)

Based on this, can you see what I'm doing wrong here? Or if there's a different/better way to do this, I'm open to that as well!

EDIT: Looks like when LegacyAuthProtocolsEnabled is set to False I explicitly cannot use the SharePointOnlineCredentials class at all, based on this page. Given that, looks like I need a different approach to get this access!

Kevin Pope
  • 2,964
  • 5
  • 32
  • 47
  • I use similar code in a lot of apps, so there's something wrong in the credentials passed or in the account used – Camilo Terevinto Oct 08 '18 at 17:46
  • @CamiloTerevinto Thanks for the note, but it explicity says [here](https://learn.microsoft.com/en-us/powershell/module/sharepoint-online/set-spotenant?view=sharepoint-ps) that SharePointOnlineCredentials class is blocked when LegacyAuthProtocolsEnabled is set to False (which is the case in our tenant and is not something I can change). Looks like that explains why my code doesn't work, but doesn't really provide me with an alternative to do what I need! – Kevin Pope Oct 08 '18 at 18:00

3 Answers3

9

This a very common issue which we often face when LegacyAuthProtocolsEnabled is set to False. It also affects us when we have configured Multi-factor Authentication (MFA) in our tenant.

To authenticate to SPO then, we use the GetWebLoginClientContext method of SharePoint PnP Core library which is available as a nuget package.

So, modify your code as below:

AuthenticationManager authManager = new AuthenticationManager();
using (var context = authManager.GetWebLoginClientContext(URI))
{
    context.Load(context.Web, web => web.Title);
    context.ExecuteQuery();
    Console.WriteLine("Your site title is: " + context.Web.Title);
}

Add SharePointPnPCoreOnline Nuget package in your console application. After that, you will be able to login to the SPO env. Basically, this will give you a prompt to enter your details in a browser window.

Gautam Sheth
  • 2,442
  • 1
  • 17
  • 16
  • 1
    Thanks Gautam, this is helpful. Unfortunately this is going to be running as an unmonitored job on a server instead of as a client application, so I need a way to login programmatically without any user interaction. Based on this, I'm looking more into the PnP Core library's other auth functions, thanks for the tip! – Kevin Pope Oct 09 '18 at 18:34
1

the daemon scenario you are describing can be achieved using an Azure App for authenticating and getting permissions via a JWT token. For a walkthrough see Authenticating to Azure AD non-interactively using a username & password or Windows Integrated Authentication

If you have not worked with azure apps before I recommend you take some time to get acquainted. They are basically trusted principals you create and approve so that your apps use their permissions to perform SharePoint operations.

I am also no longer recommending CSOM for any new developments, especially for SP Online as I believe Microsoft is (more or less openly) retiring this API. Use the PnP library as Gautam also recommended, this is being kept up to date and is a great wrapper for the REST API and also Managed Metadate etc.

0xced
  • 25,219
  • 10
  • 103
  • 255
Razvan
  • 342
  • 2
  • 9
  • Thanks - just the direction I needed. Ended up creating an Azure AD Web API app that was granted permissions to the O365 services I needed, then auth via certificates. – Kevin Pope Oct 11 '18 at 23:36
  • 2
    FYI Microsoft is not retiring CSOM and has developed CSOM for .NET Standard - https://learn.microsoft.com/sharepoint/dev/sp-add-ins/using-csom-for-dotnet-standard. –  Nov 11 '20 at 15:10
  • 2
    In 2020 it's the [PnP Sites Core library that was retired](https://github.com/pnp/PnP-Sites-Core) while CSOM was updated. CSOM follows the REST API model so there's little chance it will be retired. PnP libraries though have always been volatile, often overengineered and always abandoned after a couple of years. After all, if a pattern is so useful, why not move it into the main library itself? This isn't specific to SharePoint - the Data Access Block released ~ 2005 was incorporated into ADO.NET 2.0 in 2008. The logging block essentially abandon when Lg4Net came out – Panagiotis Kanavos Apr 05 '21 at 06:36
-3
string siteUrl="your Site Url";
    string username="UserName";
    string password="Password";

    public static ClientContext CreateClientContext(string siteUrl,string username,string password)
    {
        ClientContext context = new ClientContext(siteUrl);
        var securePassword = new SecureString();
        foreach (var chr in password) securePassword.AppendChar(chr);
        context.Credentials = new SharePointOnlineCredentials(username, securePassword);
        return context;
    }
TylerH
  • 20,799
  • 66
  • 75
  • 101
  • 1
    Thanks Rakesh, but this is essentially the same non-working code I provided in my question, and I explicitly said at the end that the `SharePointOnlineCredentials` object cannot be used in our environment. – Kevin Pope Oct 10 '18 at 16:51
  • This code doesn't authenticate anything. It creates a `SharePointOnlineCredentials` object that can be used to pass the credentials during authentication. – Panagiotis Kanavos Apr 05 '21 at 06:26