我正在构建一个 WPF 应用程序,我想在单击按钮或在下拉列表中时从 Azure AD 应用程序检索所有用户的名称。我在 ASP.NET Core Web API 中编写了一个 HttpGet 方法来实现此目的,并使用 Postman(使用 OAuth 2.0)对其进行了测试,我可以成功地看到所有用户的名称。但是,当我从 WPF 应用程序调用 HttpGet 端点时,我遇到了错误。
这是我的 HttpGet 端点,它在 Postman 中测试时从 Azure AD 应用程序返回所有用户的名称(OAuth 2.0,其中我提供了客户端 ID、客户端密码、范围等)-
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly ITokenAcquisition _tokenAcquisition;
private readonly ILogger<UsersController> _logger;
public UsersController(ITokenAcquisition tokenAcquisition, ILogger<UsersController> logger)
{
_tokenAcquisition = tokenAcquisition;
_logger = logger;
}
[HttpGet]
public async Task<IActionResult> GetUsers()
{
try
{
var token = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { "User.ReadBasic.All", "User.Read" });
var graphClient = new GraphServiceClient(new AuthProvider(token));
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, "An error occurred while fetching users.");
return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while fetching users.");
}
}
private class AuthProvider : IAuthenticationProvider
{
private readonly string _token;
public AuthProvider(string token)
{
_token = token;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token);
await Task.CompletedTask;
}
}
}
在我的WPF应用程序中调用API(我不确定我是否使用了正确的方法)-
public partial class MainWindow : Window
{
private static readonly HttpClient client = new HttpClient();
private static IConfidentialClientApplication confidentialClientApp;
public MainWindow()
{
InitializeComponent();
// Initialize MSAL ConfidentialClientApplication with client secret
confidentialClientApp = ConfidentialClientApplicationBuilder.Create("47......")
.WithClientSecret("gF......")
.WithAuthority(AzureCloudInstance.AzurePublic, "d81e......")
.WithRedirectUri("http://localhost")
.Build();
}
private async void FetchDataButton_Click(object sender, RoutedEventArgs e)
{
try
{
// Acquire token silently
string[] scopes = new string[] { "api://47....../.default" };
AuthenticationResult authResult = await confidentialClientApp.AcquireTokenForClient(scopes).ExecuteAsync();
// Set the token in the request header
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
// Make the HTTP GET request
string apiUrl = "https://localhost:7159/api/Users";
var response = await client.GetAsync(apiUrl);
if (response.IsSuccessStatusCode)
{
string responseData = await response.Content.ReadAsStringAsync();
MessageBox.Show(responseData, "API Response", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
MessageBox.Show("Error: " + response.ReasonPhrase, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
catch (MsalException msalEx)
{
MessageBox.Show("Authentication error: " + msalEx.Message, "Authentication Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (Exception ex)
{
MessageBox.Show("Exception: " + ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
但我收到一个错误 - MsalUiRequiredException:AADSTS50058:已发送静默登录请求,但没有用户登录。
我可以做什么来调用此 HttpGet 端点并在我的 WPF 应用程序中显示用户名列表?
就我而言,我注册了一个应用程序并公开了一个具有如下范围的 API:
在 API 权限选项卡中,我添加了适用于客户端凭据流程的
Application类型的
User.Read.All
权限:
现在,我创建了另一个名为
MyWpfApp
的应用程序,并授予了公开的 API 权限,如下所示:
请查找以下适用于我的案例的代码示例:
UsersController.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
using Microsoft.Identity.Web;
using Microsoft.Kiota.Abstractions.Authentication;
using Microsoft.Kiota.Abstractions;
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly ITokenAcquisition _tokenAcquisition;
public UsersController(ITokenAcquisition tokenAcquisition)
{
_tokenAcquisition = tokenAcquisition;
}
[HttpGet]
public async Task<IActionResult> GetUsers()
{
try
{
var token = await _tokenAcquisition.GetAccessTokenForAppAsync("https://graph.microsoft.com/.default");
var graphClient = new GraphServiceClient(new AuthProvider(token));
// Fetch users with displayName
var users = await graphClient.Users.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Select = new string[] { "displayName" };
});
if (users == null || users.Value == null)
{
return StatusCode(500, "Error retrieving users: Users response is null");
}
var usersList = users.Value
.Where(u => u.DisplayName != null)
.Select(u => u.DisplayName)
.ToList();
return Ok(usersList);
}
catch (System.Exception ex)
{
return StatusCode(500, $"Error retrieving users: {ex.Message}");
}
}
private class AuthProvider : IAuthenticationProvider
{
private readonly string _token;
public AuthProvider(string token)
{
_token = token;
}
public Task AuthenticateRequestAsync(RequestInformation request, Dictionary<string, object>? additionalAuthenticationContext = null, CancellationToken cancellationToken = default)
{
request.Headers.Add("Authorization", $"Bearer {_token}");
return Task.CompletedTask;
}
}
}
当我运行上述 API 时,我可以通过调用以下端点成功检索用户:
GET https://localhost:7261/api/Users
回复:
要在单击按钮时在 WPF 应用程序中获得相同的响应,您可以使用以下示例代码文件:
MainWindow.xaml:
<Window x:Class="MyWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="My WPF App" Height="350" Width="525">
<Grid>
<Button Name="FetchDataButton" Content="Fetch Users" Click="FetchDataButton_Click" Width="150" Height="40" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,10,0,0" />
<ListBox Name="UsersListBox" Width="400" Height="200" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,60,0,0" />
</Grid>
</Window>
MainWindow.xaml.cs:
using Microsoft.Identity.Client;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Windows;
namespace MyWpfApp
{
public partial class MainWindow : Window
{
private static readonly HttpClient client = new HttpClient();
private static IConfidentialClientApplication confidentialClientApp;
public MainWindow()
{
InitializeComponent();
confidentialClientApp = ConfidentialClientApplicationBuilder.Create("wpfappId")
.WithClientSecret("wpfappsecret")
.WithAuthority(new Uri("https://login.microsoftonline.com/tennatId"))
.Build();
}
private async void FetchDataButton_Click(object sender, RoutedEventArgs e)
{
try
{
string[] scopes = { "api://myuserapiappId/.default" };
AuthenticationResult authResult = await confidentialClientApp.AcquireTokenForClient(scopes).ExecuteAsync();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
string apiUrl = "https://localhost:7261/api/Users";
var users = await client.GetFromJsonAsync<string[]>(apiUrl);
UsersListBox.Items.Clear();
if (users != null)
{
foreach (var user in users)
{
UsersListBox.Items.Add(user);
}
}
else
{
MessageBox.Show("No users found.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
catch (MsalException msalEx)
{
MessageBox.Show("Authentication error: " + msalEx.Message, "Authentication Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}
回复: