MSAL+Azure AD认证:React调用ASP.NET Core API令牌无效问题排查
React+MSAL调用ASP.NET Core API时令牌无效的原因及修复方案
问题背景
我正尝试通过MSAL实现Azure AD认证,从React前端调用ASP.NET Core后端API,流程如下:
- React端通过MSAL登录并获取令牌:
const { instance, accounts, inProgress } = useMsal(); const [accessToken, setAccessToken] = useState<string | null>(null); const name = accounts[0] && accounts[0].name; function RequestAccessToken() { const request = { ...loginRequest, account: accounts[0] }; instance.acquireTokenSilent(request).then((response) => { setAccessToken(response.accessToken); }).catch((e) => { instance.acquireTokenPopup(request).then((response) => { setAccessToken(response.accessToken); }); }); }
- 将令牌放入请求头调用后端接口:
const headers = new Headers(); const bearer = `Bearer ${accessToken}`; headers.append("Authorization", bearer); const options: RequestInit = { method: "GET", headers: headers }; const response = await fetch('/kpiData', options);
后端Program.cs初始配置:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")); builder.Services.AddControllersWithViews(options => { var policy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(policy)); });
补充配置:
- appsettings.json:
"AzureAd": { "Instance": "https://login.microsoftonline.com", "ClientId": "<Client ID from Azure App Registration>", "TenantId": "common", "Audience": "Client ID from Azure App Registration>" }
- authConfig.ts:
export const msalConfig: Configuration = { auth: { clientId: "<Client ID from Azure App Registration>", authority: "https://login.microsoftonline.com/<Tenant ID>", redirectUri: "https://localhost:44493" }, cache: { cacheLocation: "sessionStorage", storeAuthStateInCookie: false, } }; export const loginRequest: RedirectRequest = { scopes: ["api://<Client ID>/User.Read"] };
但调用接口时返回令牌无效,请问原因及Program.cs的正确配置方式?
令牌无效的核心原因
- 租户ID不匹配:前端authConfig用了具体的
<Tenant ID>,但后端appsettings里的TenantId设为common,导致令牌的发行人与后端验证的租户不一致,直接触发验证失败。 - 配置拼写错误:appsettings的
Audience字段结尾多了一个>符号,导致后端验证令牌受众时无法匹配。 - 中间件缺失:大概率没在Program.cs中添加
UseAuthentication和UseAuthorization中间件,认证逻辑根本没生效。
修复方案及正确配置
1. 修正appsettings.json配置
把TenantId改成和前端一致的具体租户ID,同时去掉Audience的多余符号,注意受众格式要和前端scope前缀一致:
"AzureAd": { "Instance": "https://login.microsoftonline.com", "ClientId": "<你的API应用注册Client ID>", "TenantId": "<和前端一致的租户ID>", "Audience": "api://<你的API应用注册Client ID>" }
2. 确保前端scope的有效性
前端请求的scope必须是API应用注册中已暴露的权限,格式要严格匹配api://<API Client ID>/<权限名称>,比如自定义权限或默认的User.Read。
3. ASP.NET Core Program.cs完整配置
必须添加认证和授权中间件,顺序不能颠倒,完整配置如下:
var builder = WebApplication.CreateBuilder(args); // 添加Azure AD认证服务 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")) .EnableTokenAcquisitionToCallDownstreamApi() .AddInMemoryTokenCaches(); // 全局授权策略,要求所有接口必须认证 builder.Services.AddControllersWithViews(options => { var policy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(policy)); }); var app = builder.Build(); // 启用认证和授权中间件,顺序:先认证,后授权 app.UseAuthentication(); app.UseAuthorization(); // 映射控制器路由 app.MapControllers(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
额外排查步骤
- 用本地JWT解析工具解码获取到的accessToken,检查
iss(发行人)、aud(受众)、tid(租户ID)是否和后端配置一致。 - 确认API应用注册中已正确配置“公开API”权限,且前端应用已被授予该权限。
内容的提问来源于stack exchange,提问作者Julien
相关产品推荐
相关产品推荐

