使用MSAL、OpenID Connect调用Azure受保护Web API时遇401未授权
客户端应用访问部署在Azure上的受保护Web API,打开应用时已获取Access Token并添加到请求头,但API返回401 unauthorized。
客户端获取Access Token的代码
public async Task<string> GetToken() { var authorizationUri = new Uri($@"https://login.microsoftonline.com/{AUTHORITY}"); var pca = PublicClientApplicationBuilder.Create(CLIENTID) .WithAuthority(authorizationUri) //.WithAuthority(AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount) .WithRedirectUri(REDIRECTURL).Build(); // 获取账户 var accounts = await pca.GetAccountsAsync(); var accountToLogin = PublicClientApplication.OperatingSystemAccount; try { // 静默获取Token var authResult = await pca.AcquireTokenSilent(new[] { "api://..api-id../access_as_user" }, accountToLogin) .ExecuteAsync(); return authResult.AccessToken; } catch (MsalUiRequiredException) { // 交互式获取Token var authResult = await pca.AcquireTokenInteractive(new[] { "api://..api-id../access_as_user" }) .WithAccount(accountToLogin) .ExecuteAsync(); return authResult.AccessToken; } }
调用Web API的代码
public async Task<string> GetForecast() { var access = new ServiceAccess(); var token = await access.GetToken(); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); using HttpResponseMessage response = await client.GetAsync("https://my-api.azurewebsites.net/api/weatherforecast"); response.EnsureSuccessStatusCode(); ... }
Web API的认证配置
builder.Services.AddCors(options => { options.AddPolicy(name: "mypolicy", policy => { policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod(); }); }); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => builder.Configuration.Bind("AzureAd", options));
已检查的Token内容(jwt.io验证无异常)
{ "aud": "my web API id", "iss": "https://login.microsoftonline.com/my-tenant-id/v2.0", "iat": 1675018002, "nbf": 1675018002, "exp": 1675022027, "aio": "....", "azp": "my web API id", "azpacr": "0", "idp": "live.com", "name": "...", "oid": "some guid", "preferred_username": "...@gmail.com", "rh": "???", "scp": "access_as_user", "sub": "???", "tid": "my tenant id", "uti": "???", "ver": "2.0" }
已排查的情况
- 排除受众错误:Token的
aud字段已包含Web API的ID - 已尝试官方ASP.NET Web API认证手册,但问题未解决
解决方案建议
检查Web API的AzureAd配置项
确认appsettings.json中的AzureAd配置是否正确,核心字段需与Token匹配:ClientId:必须等于Token中的aud值(Web API的ID)TenantId:必须等于Token中的tid值Instance:应为https://login.microsoftonline.com/
示例正确配置:
"AzureAd": { "Instance": "https://login.microsoftonline.com/", "TenantId": "你的租户ID", "ClientId": "你的Web API ID" }显式配置Token发行方验证
虽然Token内容正常,但Web API可能未正确验证发行方,可在配置中强制指定:builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { builder.Configuration.Bind("AzureAd", options); options.TokenValidationParameters.ValidIssuer = $"https://login.microsoftonline.com/{builder.Configuration["AzureAd:TenantId"]}/v2.0"; options.TokenValidationParameters.ValidateIssuer = true; });确认Web API的账户访问权限
Token中idp为live.com,说明是个人微软账户登录,需检查:- Azure门户中Web API的应用注册,"支持的账户类型"是否设置为"任何组织目录中的账户和个人微软账户"
- 客户端应用已被授予
access_as_user权限,且该权限已完成管理员或用户同意
检查认证中间件顺序
确保在Program.cs中正确添加中间件,顺序不可颠倒:app.UseAuthentication(); app.UseAuthorization();验证控制器授权特性
确认API控制器或方法上添加了[Authorize]特性,无错误策略配置:[ApiController] [Route("api/[controller]")] [Authorize] public class WeatherForecastController : ControllerBase { // ... }捕获认证失败详情
添加事件处理获取具体错误原因,便于排查:.AddJwtBearer(options => { builder.Configuration.Bind("AzureAd", options); options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { // 记录日志或输出错误信息 Console.WriteLine($"认证失败: {context.Exception.Message}"); return Task.CompletedTask; } }; });
内容的提问来源于stack exchange,提问作者Prokurors
相关产品推荐
相关产品推荐

