SharePoint Online:WinForms 应用程序中的令牌和多因素身份验证

问题描述 投票:0回答:1

我有一个使用多重身份验证的 SharePoint 在线网站:我会看到一个登录屏幕,我可以在其中提供我的用户名和密码。之后,我会看到另一个弹出窗口,我可以在其中选择第二种身份验证方法,例如 Microsoft Authenticator、RSA 令牌或智能卡等。

我的 WinForm 应用程序背后的想法是也以上述相同的方式进行身份验证,一旦通过身份验证,我将使用我的 WinForm 应用程序在 SharePoint 网站上执行一些操作(请参阅下面的 WebFullUrl)。 我的知识相当有限,但我知道在实际访问该网站之前我需要获得令牌。请随时纠正我。 :)

我目前面临的最大问题是我的应用程序获取令牌。 这是我到目前为止的项目。

using System.Security.Principal;
using System.Security;
using System.Diagnostics;
using PnP.Framework;
using Microsoft.SharePoint.Client;
using Microsoft.Graph;
using System.Globalization;
using Microsoft.Identity.Client;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using static System.Formats.Asn1.AsnWriter;
using System.Windows.Interop;
using Microsoft.AspNetCore.Components;
using Microsoft.IdentityModel.Abstractions;

namespace AuthDemoWinForms
{
      public partial class MainForm : System.Windows.Forms.Form
      {
          private static readonly string[] scopesArray = ["User.Read", "Contacts.Read"];
          private static readonly string Username = "[email protected]";
          private static readonly string Password = "ThisIsMyPassword";
          private static readonly string AuthEndPoint = "https://login.microsoftonline.com/common/oauth2/token";
          private static readonly string ClientId = "00000003-0000-0ff1-ce00-000000000000";
          private static IPublicClientApplication? SharePointApplication = null;
          private static readonly string WebFullUrl = "https://domainname.sharepoint.com/teams/DEFILES/Shared%20Documents/Forms/AllItems.aspx";
    
          public MainForm()
          {
    
              InitializeComponent();
    
              SharePointApplication = PublicClientApplicationBuilder.Create(ClientId)
                  .WithDefaultRedirectUri()
                  .WithLogging(new IdentityLogger(EventLogLevel.Warning), enablePiiLogging: false)
                  .Build();
    
          }
    
          private void LoginButton_Click(object sender, EventArgs e)
          {
              if (SharePointApplication != null)
              {
                  Task<AuthenticationResult?> authenticationResultTask = GetTokenInteractively(SharePointApplication);
                  authenticationResultTask.Wait();
                  AuthenticationResult? authenticationResult = authenticationResultTask.Result;
                  OutputTextBox.Text = authenticationResult == null ? string.Empty : authenticationResult.Account.Username;
              }
          }
          private static async Task<AuthenticationResult?> GetTokenInteractively(IPublicClientApplication? publicClientApplication)
          {
              try
              {
                  return await publicClientApplication!.AcquireTokenInteractive(scopesArray)
                      .WithParentActivityOrWindow(new WindowInteropHelper(System.Windows.Application.Current.MainWindow))
                      .ExecuteAsync();
              }
              catch (Exception ex)
              {
                  Debug.WriteLine($"InnerException : {ex.InnerException}\nMessage : {ex.Message}\nStackTrace : {ex.StackTrace}");
                  return null;
              }
          }
          private static async Task<AuthenticationResult?> Login()
          {
              try
              {
    
              }
              catch (Exception ex)
              {
              }
              return null;
          }
      }
}

我有一个关于

.WithParentActivityOrWindow(new WindowInteropHelper(System.Windows.Application.Current.MainWindow))
的问题。

这是否会向我显示身份验证弹出窗口,或者我是否仍然需要构建一个能够处理此问题的对话? 另外,我需要在何时何地输入用户名和密码?

作为额外说明,异步任务似乎未完成。

我迷路了!

c# winforms azure-active-directory sharepoint-online windows-authentication
1个回答
0
投票

最初,我注册了一个 Azure AD 应用程序,并在获得 管理员同意的情况下授予了 SharePoint API 权限

enter image description here

确保在移动和桌面应用程序平台中添加重定向URI并启用公共客户端应用程序,如下所示:

enter image description here

就我而言,我使用了 C Matkas 提供的这个 GitHub 示例,并且能够获取访问令牌来调用 SharePoint API,只需对代码进行少量更改。

Form1.cs:

using Microsoft.Identity.Client;
using System;
using System.Linq;
using System.Reflection.Emit;
using System.Security.Principal;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AuthDemoWinForms
{
    public partial class Form1 : Form
    {
        private static bool loggeIn = false;
        private static readonly string[] scopes = { "https://your_domain_name.sharepoint.com/.default" };

        public Form1()
        {
            InitializeComponent();
        }

        private string GetCurrentPrincipal()
        {
            var currentUser = WindowsIdentity.GetCurrent();
            return currentUser.Name;
        }

        private async void Button1_Click(object sender, EventArgs e)
        {
            if (!loggeIn)
            {
                // Windows Auth
                WindowsIdentity current = WindowsIdentity.GetCurrent();
                WindowsPrincipal windowsPrincipal = new WindowsPrincipal(current);
                label1.Text = windowsPrincipal.Identity.Name;

                AuthenticationResult authResult = await Program.PublicClientApp.AcquireTokenInteractive(scopes)
                    .ExecuteAsync();

                // Print username if available
                if (authResult?.Account != null)
                {
                    label4.Text = authResult.Account.Username;
                }
                else
                {
                    label4.Text = "Unknown";
                }

                // Print access token if available
                if (authResult != null && authResult.AccessToken != null)
                {
                    textBoxToken.Text = "Access Token: " + authResult.AccessToken;
                }
                else
                {
                    textBoxToken.Text = "Access Token: Not available";
                }

                button1.Text = "Log Out";
                loggeIn = true;
            }
            else
            {
                await Logout();
                button1.Text = "Log In";
                loggeIn = false;
                textBoxToken.Text = ""; // Clear access token when logging out
            }
        }

        private async Task<AuthenticationResult> Login()
        {
            AuthenticationResult authResult = null;
            var accounts = await Program.PublicClientApp.GetAccountsAsync();
            var firstAccount = accounts.FirstOrDefault();

            try
            {
                authResult = await Program.PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
                    .ExecuteAsync();
            }
            catch (MsalUiRequiredException ex)
            {
                System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");

                try
                {
                    authResult = await Program.PublicClientApp.AcquireTokenInteractive(scopes)
                        .WithAccount(accounts.FirstOrDefault())
                        .WithPrompt(Prompt.SelectAccount)
                        .ExecuteAsync();
                }
                catch (MsalException msalex)
                {
                    label1.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
                }
            }
            catch (Exception ex)
            {
                label1.Text = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}";
            }
            return authResult;
        }

        private async Task Logout()
        {
            var accounts = await Program.PublicClientApp.GetAccountsAsync();
            if (accounts.Any())
            {
                try
                {
                    await Program.PublicClientApp.RemoveAsync(accounts.FirstOrDefault());
                    this.label1.Text = "User has signed-out";
                    this.label4.Text = "User has signed-out";
                }
                catch (MsalException ex)
                {
                    throw new Exception($"Error signing-out user: {ex.Message}");
                }
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }
    }
}

Form1.Designer.cs:

namespace AuthDemoWinForms
{
    partial class Form1
    {
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.button1 = new System.Windows.Forms.Button();
            this.label4 = new System.Windows.Forms.Label();
            this.label5 = new System.Windows.Forms.Label();
            this.textBoxToken = new System.Windows.Forms.TextBox(); 
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(145, 34);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(62, 13);
            this.label1.TabIndex = 0;
            this.label1.Text = "Anonymous";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(23, 34);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(116, 13);
            this.label2.TabIndex = 1;
            this.label2.Text = "Current Windows User:";
            // 
            // button1
            // 
            this.button1.BackColor = System.Drawing.Color.SkyBlue;
            this.button1.Location = new System.Drawing.Point(26, 103);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(129, 23);
            this.button1.TabIndex = 2;
            this.button1.Text = "Login";
            this.button1.UseVisualStyleBackColor = false;
            this.button1.Click += new System.EventHandler(this.Button1_Click);
            // 
            // label4
            // 
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(145, 63);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(62, 13);
            this.label4.TabIndex = 4;
            this.label4.Text = "Anonymous";
            // 
            // label5
            // 
            this.label5.AutoSize = true;
            this.label5.Location = new System.Drawing.Point(23, 63);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(90, 13);
            this.label5.TabIndex = 5;
            this.label5.Text = "Current Azure AD User: ";
            // 
            // textBoxToken
            // 
            this.textBoxToken.Location = new System.Drawing.Point(26, 143);
            this.textBoxToken.Multiline = true;
            this.textBoxToken.Name = "textBoxToken";
            this.textBoxToken.ReadOnly = true;
            this.textBoxToken.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.textBoxToken.Size = new System.Drawing.Size(300, 100); // Adjust size as needed
            this.textBoxToken.TabIndex = 6;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.textBoxToken);
            this.Controls.Add(this.label5);
            this.Controls.Add(this.label4);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Label label4;
        private System.Windows.Forms.Label label5;
        private System.Windows.Forms.TextBox textBoxToken; 
    }
}

当我运行 WinForms 应用程序并单击

Login
按钮时,弹出窗口 似乎像这样登录:

enter image description here

如果为用户启用了MFA,它将根据配置的方法要求进行双因素身份验证:

enter image description here

身份验证成功后,屏幕下方将显示已登录的 Azure AD 用户名以及 访问令牌值:

enter image description here

要确认这一点,您可以通过将访问令牌粘贴到 jwt.ms 网站来解码它,并检查

aud
scp
声明以验证权限:

enter image description here

我有一个名为

sritestsite10
的 SharePoint 网站,其中 共享文档 文件夹中包含以下文件:

enter image description here

当我使用生成的令牌通过以下查询在 Postman 中调用 SharePoint API 时,我成功获得了 response,其中包含文件详细信息,如下所示:

GET https://domain_name.sharepoint.com/sites/sritestsite10/_api/web/GetFolderByServerRelativeUrl('Shared Documents')/Files

回复:

enter image description here

参考: 使用 Azure AD 对 WinForms(本机)应用程序 (cmatskas.com) 进行现代身份验证,作者:C Matkas

© www.soinside.com 2019 - 2024. All rights reserved.