You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WPF应用中获取Azure AD用户姓名列表并解决认证报错

问题分析与解决方案

你遇到的AADSTS50058错误核心矛盾是认证流不匹配:

  • Web API中的GetAccessTokenForUserAsync是基于用户委派权限的逻辑,需要当前存在已登录的用户上下文,才能代表用户获取访问Microsoft Graph的令牌。
  • 但WPF中使用的AcquireTokenForClient属于客户端凭证流,是服务对服务的无用户上下文认证,导致API尝试获取用户令牌时,因无用户登录抛出错误。

以下是两种针对性解决方案:


方案一:修改WPF使用用户交互登录流(推荐,匹配原API设计)

该方案让WPF先引导用户登录,获取用户上下文的访问令牌后再调用API,符合原API的用户委派权限逻辑。

修改后的WPF代码

public partial class MainWindow : Window
{
    private static readonly HttpClient client = new HttpClient();
    private static IPublicClientApplication publicClientApp;

    public MainWindow()
    {
        InitializeComponent();

        // 桌面应用属于公共客户端,使用PublicClientApplication而非ConfidentialClientApplication
        publicClientApp = PublicClientApplicationBuilder.Create("47......")
            .WithAuthority(AzureCloudInstance.AzurePublic, "d81e......") // 替换为你的租户ID
            .WithRedirectUri("http://localhost") // 桌面应用默认重定向URI
            .Build();
    }

    private async void FetchDataButton_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            // 替换为你API中定义的用户委派作用域
            string[] scopes = new string[] { "api://47....../access_as_user" };

            AuthenticationResult authResult = null;
            var accounts = await publicClientApp.GetAccountsAsync();

            // 先尝试静默获取令牌(若用户之前登录过)
            try
            {
                authResult = await publicClientApp.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
            }
            catch (MsalUiRequiredException)
            {
                // 静默失败,弹出登录界面
                authResult = await publicClientApp.AcquireTokenInteractive(scopes)
                    .WithAccount(accounts.FirstOrDefault())
                    .WithParentActivityOrWindow(new WindowInteropHelper(this).Handle) // 绑定WPF窗口,让登录弹窗置顶
                    .ExecuteAsync();
            }

            // 设置请求头令牌
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);

            // 调用API并处理结果
            string apiUrl = "https://localhost:7159/api/Users";
            var response = await client.GetAsync(apiUrl);

            if (response.IsSuccessStatusCode)
            {
                var userNames = await response.Content.ReadFromJsonAsync<List<string>>();
                // 可将结果绑定到下拉框等控件,示例:
                // UserComboBox.ItemsSource = userNames;
                MessageBox.Show(string.Join(Environment.NewLine, userNames), "用户列表", MessageBoxButton.OK, MessageBoxImage.Information);
            }
            else
            {
                var errorDetail = await response.Content.ReadAsStringAsync();
                MessageBox.Show($"错误: {response.ReasonPhrase}\n详情: {errorDetail}", "请求失败", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        catch (MsalException msalEx)
        {
            MessageBox.Show($"认证错误: {msalEx.Message}", "认证失败", MessageBoxButton.OK, MessageBoxImage.Error);
        }
        catch (Exception ex)
        {
            MessageBox.Show($"异常: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }
}

配套配置要求

  1. 在Azure AD中,为你的API应用暴露用户委派作用域(如access_as_user),并给WPF应用授予该作用域的权限。
  2. 确保Web API的认证中间件配置正确(如使用AddMicrosoftIdentityWebApi),能正常验证用户的访问令牌。

方案二:修改Web API使用客户端凭证流(无用户上下文场景)

若你的业务不需要用户上下文,仅需服务端直接获取所有用户,可修改API使用客户端凭证流调用Graph。

修改后的Web API代码

[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
    private readonly ILogger<UsersController> _logger;
    private readonly IConfiguration _configuration;

    public UsersController(ILogger<UsersController> logger, IConfiguration configuration)
    {
        _logger = logger;
        _configuration = configuration;
    }

    [HttpGet]
    public async Task<IActionResult> GetUsers()
    {
        try
        {
            // 从配置读取Azure AD参数
            var clientId = _configuration["AzureAd:ClientId"];
            var clientSecret = _configuration["AzureAd:ClientSecret"];
            var tenantId = _configuration["AzureAd:TenantId"];

            // 客户端凭证流获取Graph令牌
            var scopes = new[] { "https://graph.microsoft.com/.default" };
            var authority = $"https://login.microsoftonline.com/{tenantId}";

            var confidentialClient = ConfidentialClientApplicationBuilder
                .Create(clientId)
                .WithClientSecret(clientSecret)
                .WithAuthority(authority)
                .Build();

            var authResult = await confidentialClient.AcquireTokenForClient(scopes).ExecuteAsync();

            // 初始化Graph客户端
            var graphClient = new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
            {
                requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
            }));

            // 分页获取用户姓名
            var usersList = new List<string>();
            var usersPage = await graphClient.Users
                .Request()
                .Select(u => new { u.DisplayName })
                .GetAsync();

            usersList.AddRange(usersPage.Select(u => u.DisplayName));

            while (usersPage.NextPageRequest != null)
            {
                usersPage = await usersPage.NextPageRequest.GetAsync();
                usersList.AddRange(usersPage.Select(u => u.DisplayName));
            }

            return Ok(usersList);

        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取用户列表时发生错误。");
            return StatusCode(StatusCodes.Status500InternalServerError, "获取用户列表时发生错误。");
        }
    }
}

配套配置要求

  1. 在Azure AD中,为Web API应用授予应用权限(如User.Read.All),并由管理员完成权限同意。
  2. WPF应用保持原AcquireTokenForClient逻辑,确保已被授予调用API的应用权限。

内容的提问来源于stack exchange,提问作者Madhura D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 15:57:10