You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MSAL+Azure AD认证:React调用ASP.NET Core API令牌无效问题排查

React+MSAL调用ASP.NET Core API时令牌无效的原因及修复方案

问题背景

我正尝试通过MSAL实现Azure AD认证,从React前端调用ASP.NET Core后端API,流程如下:

  1. 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);
        });
    });
}
  1. 将令牌放入请求头调用后端接口:
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的正确配置方式?


令牌无效的核心原因

  1. 租户ID不匹配:前端authConfig用了具体的<Tenant ID>,但后端appsettings里的TenantId设为common,导致令牌的发行人与后端验证的租户不一致,直接触发验证失败。
  2. 配置拼写错误:appsettings的Audience字段结尾多了一个>符号,导致后端验证令牌受众时无法匹配。
  3. 中间件缺失:大概率没在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 22:50:20