Andrija Peric
Andrija Peric

Reputation: 1

Querying On-premise SharePoint using Azure AD MFA through C# app

I'm trying to use Microsoft.Identity.Client and Microsoft.SharePoint.Client libraries to authenticate to an On-premise SharePoint server and then query it.

I obtain the Azure AD access token from which the SharePoint server is a part of like following:

private readonly string[] m_scopes = { "user.read", "https://sql.azuresynapse-dogfood.net/user_impersonation" };

var publicAppBuilder = PublicClientApplicationBuilder.Create("MyClientId").WithAuthority("https://login.microsoftonline.com/a******com.onmicrosoft.com"); 
 
publicAppBuilder.WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient"); 

var app = publicAppBuilder.Build();

AuthenticationResult result = null;

result = app.AcquireTokenInteractive(m_scopes).ExecuteAsync().GetAwaiter().GetResult();
if (result != null)
{
    m_mediator.AccessToken = result.AccessToken;
}

When I get the access token I put it in the request header as follows:

args.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + m_mediator.AccessToken;

Which is inside the ClientContext.ExecutingWebRequest subscribed method:

clientContext.ExecutingWebRequest += (sender, args) =>

which is triggered by

context.ExecuteQuery();

The remote server returned an error: (401) Unauthorized. or The remote server returned an error: (403) Forbidden.

How can I establish the connection? I want to avoid using app-only registration, I want to authenticate using Azure AD MFA (Interactive) method.Please note that I have all the permissions needed and I am an admin on both Azure AD where SharePoint is joined, as well on the SharePoint server itself. I authenticate through the browser just fine.

I've tried multiple things so far:

I'm expecting to Authenticate via Azure AD and then connect to SharePoint in order to query it

Upvotes: 0

Views: 1156

Answers (2)

Andrija Peric
Andrija Peric

Reputation: 1

I've found a solution.

I basically iterate through all cookies whenever a browser navigates through a new page and parse all the cookies until I get the fedAuth cookie:

  1. I created a web browser from System.Windows.Forms.WebBrowser

  2. In the WebBrowserNavigatedEventHandler for Navigated I do the following:

    if (webBrowser1.Url.AbsoluteUri == "about:blank") { return; }

    var cookieData = GetWebBrowserCookie.GetCookieInternal(webBrowser1.Url, false);

    if (string.IsNullOrEmpty(cookieData) == false) { var dict = ParseCookieData(cookieData); if (dict.ContainsKey("FedAuth") && !string.IsNullOrEmpty(dict["FedAuth"])) { m_mediator.FedAuthCookie = dict["FedAuth"]; if (dict.ContainsKey("rtFa") && !string.IsNullOrEmpty(dict["rtFa"])) { m_mediator.RtFaCookie = dict["rtFa"]; } m_mediator.UpdateConfiguration(); this.Close(); } }

The ParseCookieData method looks like this:

private IDictionary<string, string> ParseCookieData(string cookieData)
{
  var cookieDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

  if (string.IsNullOrEmpty(cookieData))
  {
    return cookieDictionary;
  }

  var values = cookieData.TrimEnd(';').Split(';');
  foreach (var parts in values.Select(c => c.Split(new[] { '=' }, 2)))
  {
    var cookieName = parts[0].Trim();

    var cookieValue = parts.Length == 1 ? string.Empty : parts[1];

    cookieDictionary[cookieName] = cookieValue;
  }

  return cookieDictionary;
}

and GetWebBrowserCookie class looks like this:

[SecurityCritical]
public static string GetCookieInternal(Uri uri, bool throwIfNoCookie)
{
  uint pchCookieData = 0;
  string url = UriToString(uri);
  uint flag = (uint)NativeMethods.InternetFlags.INTERNET_COOKIE_HTTPONLY;

  //Gets the size of the string builder   
  if (NativeMethods.InternetGetCookieEx(url, null, null, ref pchCookieData, flag, IntPtr.Zero))
  {
    pchCookieData++;
    StringBuilder cookieData = new StringBuilder((int)pchCookieData);

    //Read the cookie   
    if (NativeMethods.InternetGetCookieEx(url, null, cookieData, ref pchCookieData, flag, IntPtr.Zero))
    {
      DemandWebPermission(uri);
      return cookieData.ToString();
    }
  }

  int lastErrorCode = Marshal.GetLastWin32Error();

  if (throwIfNoCookie || (lastErrorCode != (int)NativeMethods.ErrorFlags.ERROR_NO_MORE_ITEMS))
  {
    throw new Win32Exception(lastErrorCode);
  }

  return null;
}

private static void DemandWebPermission(Uri uri)
{
  string uriString = UriToString(uri);

  if (uri.IsFile)
  {
    string localPath = uri.LocalPath;
    new FileIOPermission(FileIOPermissionAccess.Read, localPath).Demand();
  }
  else
  {
    new WebPermission(NetworkAccess.Connect, uriString).Demand();
  }
}

private static string UriToString(Uri uri)
{
  if (uri == null)
  {
    return string.Empty;
  }

  UriComponents components = (uri.IsAbsoluteUri ? UriComponents.AbsoluteUri : UriComponents.SerializationInfoString);
  return new StringBuilder(uri.GetComponents(components, UriFormat.SafeUnescaped), 2083).ToString();
}

This way we open up a pop-up C# web browser, authenticate the user through the web using MFA and then close the browser when we acquire an authentication cookie so we can continue working with HTTP requests towards the Sharepoint server.

Source: https://github.com/OceanAirdrop/SharePointOnlineGetFedAuthAndRtfaCookie

Upvotes: 0

Rukmini
Rukmini

Reputation: 15969

To resolve the error "The remote server returned an error: (401) Unauthorized", please try checking the following:

  • Check whether your URL is correct:

The SharePoint Online URL must always start with HTTPS.

$SiteURL`  `=` `"https://crescent.sharepoint.com/sites/marketing"`
  • Check if you have the right permissions to the site:

Check whether you have sufficient permissions and you are able to open the site in the browser. Make sure to have SharePoint Online Administrator Role.

  • Check whether the Legacy authentication protocol is enabled:

Make sure to enable Legacy authentication protocol in your tenant, if it is not enabled.

Reference : SharePoint Online: Fix "The remote server returned an error (401) Unauthorized" Error in PowerShell - SharePoint Diary

To resolve the error "The remote server returned an error: (403) Forbidden.", please try checking the following:

  • Make sure whether you have provided correct URL and credentials.

  • Make sure whether you have installed latest version of SharePoint Online Client Component SDK.

  • Try adding yourself to the site explicitly

  • Check the lock status of your site and unlock if it is locked.

  • Please check if any conditional access policies is enabled in your tenant.

  • If you try to connect to the Tenant Admin site, make sure the Tenant Admin URL like below:

       https://YourDomain-admin.sharepoint.com  
    

Reference : SharePoint Online: Fix "The remote server returned an error: (403) Forbidden." Error in PowerShell - SharePoint Diary.

Upvotes: 0

Related Questions