SOLVED

SharePoint Authentication from C# Application

Copper Contributor

I've been struggling a bit to get authentication working. My C# application is a COM-based Add-In for a product called Enterprise Architect. In the app, we want to copy files to/from SharePoint, and to do that, I have to get a ClientContext object for the current user. I'm currently using AuthenticationManager.GetWebLoginClientContext(), but it has started behaving oddly - it has always popped up a browser window, but the window used to go away on its own once the authentication was done. Now that window is getting "stuck" and never closes unless I close it. This is my code:

 

using ODP = OfficeDevPnP.Core;

 

ODP.AuthenticationManager authManager = new ODP.AuthenticationManager();
ctx = authManager.GetWebLoginClientContext(ContextUrl);

 

If I close the browser window, ctx ends up being null.

 

I have also created a test harness console application that calls through to this same code, and when I run the console app, the browser form does in fact go away on its own, and the code works. But when I exercise this code via Enterprise Architect's COM-based Add-In interface, which  calls code that ends up going through this same exact code, the browser window does not close on its own as described above.

 

I have also considered switching over to AuthenticationManager.GetAzureADWebApplicationAuthenticatedContext(), but that would require a way of getting an access token. I have found lots of examples online, but they ALL refer to a MicrosoftGraphHelper class that has a GetAccessTokenForCurrentUser() method. For the life of me, I cannot find a NuGet package or anything that has an implementation of this MicrosoftGraphHelper class. For example:

 

using ODP = OfficeDevPnP.Core;

 

ODP.AuthenticationManager authManager = new ODP.AuthenticationManager();
ctx = authManager.GetAzureADWebApplicationAuthenticatedContext(ContextUrl, (url) => { return (MicrosoftGraphHelper.GetAccessTokenForCurrentUser(url)); });

 

I never would have expected this to be this hard to figure out - I have burned many hours, finding many false trails related to previous ways of interfacing with SharePoint, many of which are no longer recommended. For example, that OfficeDevPnP assembly import - it was quite a while before I found something that said that SharePointPnPCoreOnline is the currently recommended way of integrating to SharePoint, and other ways are not recommended because they are no longer being maintained.

 

Perhaps the best course of action would be for someone to recommend a good tutorial for basic integration from C# to SharePoint Online.

 

Help!

9 Replies

Here's the link to the stackoverflow post that talked about SharePointPnPCoreOnline being the recommended way to integrate to SharePoint:

 

https://stackoverflow.com/questions/52707476/authentication-to-sharepoint-online-with-csom

So I dug into this a little deeper, and discovered that it is happening because the code behind the GetWebLoginClientContext() method is looking at the cookies, specifically for cookies that have "FedAuth" or "EdgeAccessCookie" in their names. If those cookies are found, and only if they are found, the form automatically closes. I created my own implementation of the method so I could step through the code, and discovered that since it is not finding those cookies, it is not closing the form. So the real issue is that those cookies are not present in the HTTP Response. At least I've got a better lead now.

More research results...

 

Using Fiddler, I can see that the FedAuth cookie DOES come back in the response, but the part of GetWebLoginClientContext that retrieves the cookies is not seeing that cookie.

 

Looking into how the cookie retrieval process works I see that the code that retrieves the cookies is using InternetGetCookieEx() implemented in wininet.dll. That function is returning a string that does NOT contain the FedAuth cookie.

 

I'm not quite sure where to go with this next, unless there is a different way to retrieve the cookies.

best response confirmed by dougb628 (Copper Contributor)
Solution

I have this working now, and the issue seems to have been related to the URL used when you get your client context object. Our SharePoint implementation has multiple levels of sites within it. We have separate sites for every customer, and then a top level site. I was attempting to get a file from a library under a customer site, and I had been using a URL that corresponded to the customer site. Once I changed the code to use the top level site (literally just the domain name) for the client context, the popup browser started getting the FedAuth cookie and things started working. So either I don't properly understand how our SharePoint is implemented, or the authentication method is extremely sensitive to the URL you use to get the cookies. The absolute URL to the file I was attempting to copy down looks something like this:

 

https://mycompany.sharepoint.com/customers/theclientname/797%20Project%20Name/In%20Process%20Templat...

 

This URL is a valid site:

https://mycompany.sharepoint.com/customers/theclientname

 

But when I used that URL with AuthenticationManager.GetWebLoginClientContext(), it failed. Switching it to use:

https://mycompany.sharepoint.com

to get the client context fixed the problem. Now, if permissions were different at these different levels, I suspect this would have failed - it would have gotten a valid client context, but suspect I could still have gotten a permissions error when trying to use OpenBinaryStream() on my file object.

 

This was a frustrating journey, and I was disappointed to not get any feedback from anyone else. Apparently my limited understanding of SharePoint and how to integrate to it is not as common as I thought it might be.

@dougb628 I'm glad you continued to add comments here as I was having the same issues.  Someone reported this as a bug in December 2019, however it is not fixed yet, https://github.com/pnp/PnP-Sites-Core/issues/2508

 

Though we all are seeing the same behavior, the underlying causes are different.  The reporter of the bug found the error when using a SharePoint Add in and his work around was to add another cookie to the list of cookies it attempted to find.  Your issue was that it wasn't providing a cookie that was in fact there.  My issue is that it freezes on the browser.Navigated call. I get pop-up browser window, but when debugging it never drops into the code under browser.Navigated.  I have no fix at the moment.  This behavior is intermittent, it will work once and then not work again until my app is closed and reopened.  No exception occur and even when I step into the browser.Navigated call, it actually looks like it just skips over the call and goes to the next line.

@kirstenrayI hope this gets resolved soon, but I actually haven't really done much with this once I got it working by just using the domain name. In our case, all of the users of this C# application have essentially the exact same access all over our SharePoint, so I think that is why it started working for me. Frankly, I still don't know that much about integrating to SharePoint, and I think it's pretty crazy that you have to launch a browser to authenticate. Granted, it is a web application, but the authentication from the OS is much more streamlined. I can open a Windows File Explorer window, and type in a SharePoint address in the address bar, and it authenticates completely transparently, probably using Kerberos or something - I don't know. Why can't my C# application authenticate in a similar manner?

@kirstenray You not alone :(.

In a meantime did you find something? I am currently debugging and like you said sometimes it works and sometimes not.

Mainly, I noticed that if in main site where I am connected if I have subsites and try to drilldown in some of subsites that, usually, does not work.

Well, it looks like that this is not connected to subsites but to how many clicks I had.

For example, it looks like that after 3-4 clicks in short period it is stopping to work and shows only empty browser window.

It is weird as sometimes if I leave it for some period it will connect to selected URL and all will work until next click :(.

Here is how I solved the problem, building on the observations above:

It's Dirty Cache. It keeps Cookies of the token for OAuth authentication through the popup browser login. The old Token is not cleared. And when the session times out because you haven’t used it for bit, it uses the current (old) token, which is now wrong, You need to refresh the token when means you need to clear the cache. In Visual Studio RGHT.CLICK Project Clean. Then run your code again, the Popup will refresh the token, and will perform the MFA authentication accordingly.
1 best response

Accepted Solutions
best response confirmed by dougb628 (Copper Contributor)
Solution

I have this working now, and the issue seems to have been related to the URL used when you get your client context object. Our SharePoint implementation has multiple levels of sites within it. We have separate sites for every customer, and then a top level site. I was attempting to get a file from a library under a customer site, and I had been using a URL that corresponded to the customer site. Once I changed the code to use the top level site (literally just the domain name) for the client context, the popup browser started getting the FedAuth cookie and things started working. So either I don't properly understand how our SharePoint is implemented, or the authentication method is extremely sensitive to the URL you use to get the cookies. The absolute URL to the file I was attempting to copy down looks something like this:

 

https://mycompany.sharepoint.com/customers/theclientname/797%20Project%20Name/In%20Process%20Templat...

 

This URL is a valid site:

https://mycompany.sharepoint.com/customers/theclientname

 

But when I used that URL with AuthenticationManager.GetWebLoginClientContext(), it failed. Switching it to use:

https://mycompany.sharepoint.com

to get the client context fixed the problem. Now, if permissions were different at these different levels, I suspect this would have failed - it would have gotten a valid client context, but suspect I could still have gotten a permissions error when trying to use OpenBinaryStream() on my file object.

 

This was a frustrating journey, and I was disappointed to not get any feedback from anyone else. Apparently my limited understanding of SharePoint and how to integrate to it is not as common as I thought it might be.

View solution in original post