我有一种情况,我可以在我的 Startup 中成功使用 GraphServiceClient,但不能在我的 Razor 页面中使用。我正在将 Microsoft Identity Web 与 Microsoft Graph 一起使用,并在此处遵循了 MS 文档,还检查了许多其他类似的问题,但我总是以同样的错误告终......
MsalUiRequiredException:没有帐户或登录提示传递给 AcquireTokenSilent 调用
请注意,我的应用注册在我的租户中,但用户帐户在客户租户中。因此,我的应用身份验证配置为“任何组织目录中的帐户(任何 Azure AD 目录 - 多租户)”和以下 API 权限...
我的设置如下,虽然它是一个多租户应用程序,但我只有一个客户,因此将 TenantId 设置为我的客户租户 ID,以获得简化的登录体验。
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<Customer Tenant Id>",
"ClientId": "<My App Reg Id>",
"ClientSecret": "<My App Reg Secret>",
"CallbackPath": "/signin-oidc"
},
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "https://graph.microsoft.com/.default"
}
我的 Startup 具有以下 ConfigureServices 方法。请注意,对 graphServiceClient.Me.Request().GetAsync() 的调用在这里有效,并从我的客户 Azure AD 返回用户信息。当用户登录到我的应用程序时,我计划使用它来用额外的用户信息(即照片)填充我的本地数据库。
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
services.AddRazorPages()
.AddMvcOptions(options => { })
.AddMicrosoftIdentityUI();
var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(',');
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters.IssuerValidator = ValidateSpecificIssuers; //restrict access for multi-tenant app
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = async context =>
{
GraphServiceClient graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async request =>
{
// Add the access token in the Authorization header of the API request.
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.TokenEndpointResponse.AccessToken);
}));
try
{
var user = await graphServiceClient.Me.Request().GetAsync(); // THIS CALL WORKS!
}
catch (Exception ex)
{
throw new UnauthorizedAccessException("Cannot get user info from AAD");
}
await Task.FromResult(0);
}
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
});
}
private string ValidateSpecificIssuers(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
var validIssuers = Configuration["AllowedIssuers"];
if (validIssuers != null && validIssuers.Split(',').Select(tid => $"https://login.microsoftonline.com/{tid}").Contains(issuer))
{
return issuer;
}
else
{
throw new SecurityTokenInvalidIssuerException("The user account does not belong to an allowed tenant");
}
}
}
这是我的 Razor 页面,但它总是在同一个图形调用上失败,即使它在我的启动文件中工作。我已经尝试将范围从“https://graph.microsoft.com/v1.0”更改为“user.read”,但在这一行仍然失败。
var user = await _graphServiceClient.Me.Request().GetAsync();
[AuthorizeForScopes(Scopes = new[] { "https://graph.microsoft.com/.default" })]
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly GraphServiceClient _graphServiceClient;
public IndexModel(ILogger<IndexModel> logger, GraphServiceClient graphServiceClient)
{
_logger = logger;
_graphServiceClient = graphServiceClient;
}
public async void OnGet()
{
try
{
var user = await _graphServiceClient.Me.Request().GetAsync(); // THIS CALL FAILS!
using (var photoStream = await _graphServiceClient.Me.Photo.Content.Request().GetAsync())
{
byte[] photoByte = ((MemoryStream)photoStream).ToArray();
ViewData["photo"] = Convert.ToBase64String(photoByte);
}
ViewData["name"] = user.DisplayName;
}
catch (Exception e)
{
_logger.LogError(e.Message);
}
}
}
这是错误的堆栈跟踪。
Microsoft.Graph.ServiceException
HResult=0x80131500
Message=Code: generalException
Message: An error occurred sending the request.
Source=Microsoft.Graph.Core
StackTrace:
at Microsoft.Graph.HttpProvider.<SendRequestAsync>d__19.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Graph.HttpProvider.<SendAsync>d__18.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Graph.BaseRequest.<SendRequestAsync>d__40.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Graph.BaseRequest.<SendAsync>d__34`1.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Graph.UserRequest.<GetAsync>d__5.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Test365.Pages.IndexModel.<OnGet>d__3.MoveNext() in C:\Users\ojmcf\source\repos\Test365\Pages\Index.cshtml.cs:line 28
This exception was originally thrown at this call stack:
[External Code]
Inner Exception 1:
MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.
Inner Exception 2:
MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.