Blazor WASM托管方案:如何用客户端令牌在服务端调用MS Graph API?
Blazor WASM客户端登录后,ASP.NET Core Web API调用MS Graph API的配置方案及问题解决
客户端应用配置
客户端 - Program.cs
builder.Services.AddMsalAuthentication(options => { builder.Configuration.Bind("AzureAd", options.ProviderOptions); }); builder.Services .AddHttpClient(HttpClients.SERVER_API, client => { client.BaseAddress = new Uri(appSettings.ServerApi.Url); }) // 配置客户端为所有发送到授权URL的请求,在'Authorization'头中添加JWT令牌 .AddHttpMessageHandler(sp => sp.GetRequiredService<AuthorizationMessageHandler>() .ConfigureHandler( authorizedUrls: [ appSettings.ServerApi.Url ], scopes: [ appSettings.ServerApi.AccessScope ] ));
客户端 - appsettings.json
"AzureAd": { "Authentication": { "Authority": "https://login.microsoftonline.com/你的租户ID", "ClientId": "客户端应用ID" }, "DefaultAccessTokenScopes": [ "api://服务端应用ID/API.Access", "https://graph.microsoft.com/User.Read" ] }
服务端应用配置
服务端 - Program.cs
var builder = WebApplication.CreateBuilder(args); builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration) .EnableTokenAcquisitionToCallDownstreamApi() .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApis:MicrosoftGraph")) .AddInMemoryTokenCaches();
服务端 - appsettings.json
"AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "你的租户域名.onmicrosoft.com", "TenantId": "你的租户ID", "ClientId": "服务端应用ID", "ClientSecret": "服务端应用密钥", "Scopes": "API.Access", "CallbackPath": "/signin-oidc" }, "DownstreamApis": { "MicrosoftGraph": { "BaseUrl": "https://graph.microsoft.com/v1.0", "Scopes": "User.Read" } },
服务端 - 调用Graph API示例
using Microsoft.AspNetCore.Mvc; using Microsoft.Graph; using Microsoft.Identity.Web; namespace ChatPortal.Server.Controllers; [Route("api/[controller]")] [ApiController] public class TestController : ControllerBase { private readonly GraphServiceClient _graphClient; public TestController(GraphServiceClient graphClient) { _graphClient = graphClient; } [HttpGet("graph")] public async Task<IActionResult> GraphTest() { var user = await _graphClient.Me.GetAsync(); return Ok(user); } }
Azure门户配置
客户端应用注册
- API权限:
- 服务端应用(MyServerApp):API.Access,类型:委托权限,状态:已授予管理员同意
- Microsoft Graph:User.Read,类型:委托权限,状态:已授予管理员同意
服务端应用注册
- 暴露API:
- 添加范围:
api://服务端应用ID/API.Access - 授权客户端应用:添加客户端应用ID,授予对上述范围的访问权限
- 添加范围:
遇到的问题及解决方法
问题1:移除客户端DefaultAccessTokenScopes中的https://graph.microsoft.com/User.Read后调用Graph报错
错误信息:
[12:47:58 ERR] 请求执行过程中发生未处理异常。 Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException: IDW10502: 因需要用户交互验证,抛出了MsalUiRequiredException。 ---> MSAL.NetCore.4.66.1.0.MsalUiRequiredException: ErrorCode: invalid_grant Microsoft.Identity.Client.MsalUiRequiredException: AADSTS65001: 用户或管理员未同意使用ID为'你的应用ID'、名称为'ChatPortal.Server'的应用。请为此用户和资源发送交互式授权请求。
原因:服务端默认尝试用自身身份获取Graph令牌,未使用客户端传递的用户令牌走委托(OBO)流程,导致缺少用户授权。
问题2:保留客户端DefaultAccessTokenScopes中的User.Read后报错
错误信息:
请求URL: https://login.microsoftonline.com/你的租户ID/oauth2/v2.0/token 无效请求: AADSTS28000: 输入参数scope的值无效,因为它包含多个资源。Scope api://服务端应用ID/API.Access https://graph.microsoft.com/User.Read openid profile offline_access 无效。
原因:Azure AD v2.0令牌端点不允许单次请求包含多个资源的scope,客户端不能同时请求服务端API和Graph的权限。
正确解决方案
1. 客户端配置调整
客户端仅请求服务端API的scope,移除Graph相关scope:
"AzureAd": { "Authentication": { "Authority": "https://login.microsoftonline.com/你的租户ID", "ClientId": "客户端应用ID" }, "DefaultAccessTokenScopes": [ "api://服务端应用ID/API.Access" ] }
2. 服务端启用OBO流程
服务端需配置为基于客户端传递的用户令牌,通过OBO流程获取Graph访问令牌,现有Program.cs配置已支持该流程,只需补充服务端应用的Graph权限:
- 在Azure门户的服务端应用注册中,添加Microsoft Graph的
User.Read委托权限,并授予管理员同意。
3. 控制器调用Graph API
直接注入GraphServiceClient调用即可,Microsoft.Identity.Web会自动完成OBO令牌获取:
[HttpGet("graph")] public async Task<IActionResult> GraphTest() { try { var user = await _graphClient.Me.GetAsync(); return Ok(new { 显示名称 = user.DisplayName, 邮箱 = user.Mail }); } catch (Exception ex) { return BadRequest(ex.Message); } }
内容的提问来源于stack exchange,提问作者kglundgren
相关产品推荐
相关产品推荐

