通过 C# 应用使用 Azure AD MFA 查询 On-premise SharePoint

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

我正在尝试使用 Microsoft.Identity.Client 和 Microsoft.SharePoint.Client 库对 On-premise SharePoint 服务器进行身份验证,然后对其进行查询。

我获取了 Azure AD 访问令牌,SharePoint 服务器是其中的一部分,如下所示:

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;
}

当我获得访问令牌时,我将其放入请求 header 中,如下所示:

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

ClientContext.ExecutingWebRequest 订阅的方法中:

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

触发
context.ExecuteQuery();

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

如何建立连接?我想避免使用 app-only 注册,我想使用 Azure AD MFA(交互式)进行身份验证 method.Please 请注意,我拥有所需的所有权限,并且我是加入 SharePoint 的两个 Azure AD 的管理员,以及 SharePoint 服务器本身。我通过浏览器验证就好了。

到目前为止我已经尝试了多种方法:

我希望通过 Azure AD 进行身份验证,然后连接到 SharePoint 以进行查询

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

  • 检查你的URL是否正确:

SharePoint Online URL 必须始终以 HTTPS 开头。

$SiteURL`  `=` `"https://crescent.sharepoint.com/sites/marketing"`
  • 检查您是否拥有该网站的正确权限:

检查您是否有足够的权限,是否可以在浏览器中打开网站。确保具有 SharePoint Online 管理员角色。

  • 检查是否开启了Legacy认证协议:

如果未启用,请确保在您的租户中启用旧版身份验证协议。

参考 : 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:

  • 确保您是否提供了正确的 URL 和凭据。

  • 请确认您是否安装了最新版本的 SharePoint Online 客户端组件 SDK。

  • 尝试将您自己显式添加到站点

  • 检查您网站的锁定状态,如果锁定则解锁。

  • 请检查您的租户是否启用了任何条件访问策略。

  • 如果您尝试连接到租户管理站点,请确保租户管理 URL 如下所示:

       https://YourDomain-admin.sharepoint.com  
    

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

我找到了解决办法。

每当浏览器浏览新页面时,我基本上都会遍历所有 cookie 并解析所有 cookie,直到获得 fedAuth cookie:

  1. 我从 System.Windows.Forms.WebBrowser

    创建了一个网络浏览器
  2. WebBrowserNavigatedEventHandlerNavigated 我做了以下操作:

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

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

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

ParseCookieData 方法如下所示:

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;
}

和 GetWebBrowserCookie class 看起来像这样:

[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();
}

这样我们打开一个 pop-up C# 网络浏览器,使用 MFA 通过网络对用户进行身份验证,然后在我们获取身份验证 cookie 时关闭浏览器,以便我们可以继续处理对 Sharepoint 的 HTTP 请求服务器。

来源:https://github.com/OceanAirdrop/SharePointOnlineGetFedAuthAndRtfaCookie