Azure AD身份验证401错误“受众无效”AddAzureADBearer .Net Core Web Api

16
我正在尝试创建一个Azure AD身份验证的简单示例使用此示例,但我的客户端是JQuery。 我不确定为什么当令牌显示受众为https://myportal.onmicrosoft.com/test_core_web_api_spa时,我会收到关于受众无效的401错误。 这与Azure中的API定义相匹配。唯一缺少的部分是user_impersonation的自定义范围,但当我使用MSAL clientApplication.acquireTokenSilent(tokenRequest2)调用获取令牌时,我的作用域与API的完整URL和范围匹配:
const tokenRequest2 = {
    scopes: ["https://myportal.onmicrosoft.com/test_core_web_api_spa/user_impersonation"]
};

在API中建立认证,我正在使用以下代码(我注意到不多的示例使用此方法)

services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
   .AddAzureADBearer(options => Configuration.Bind("AzureAd", options));

API的配置如下:

  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "myportal.onmicrosoft.com",
    "TenantId": "my-tenant-guid",
    "ClientId": "my-api-client-guid"
  },

我注意到很多示例显示API的不同格式(我假设这些是旧版本),但在Azure中公开的API范围列在https://myportal.onmicrosoft.com/test_core_web_api_spa/user_impersonation。我还使用Azure仪表板添加了客户端的guid以访问此公开的API范围。

有任何想法我做错了什么?或者,是否有任何简单的示例使用MSAL、JQuery作为客户端和一个简单的.Net Core Web Api?似乎我找到的所有示例都过时了,或者使用不同的客户端或不同的身份验证方法。

更新以显示在Azure中公开API应用程序的设置。 我添加了一张来自Azure的图像,显示了“公开API”屏幕的设置。我添加了自定义范围user_impersonation,然后添加了客户端并授予其访问该范围的权限。正如您所看到的,我的Azure订阅没有其他人看到的api://guid格式。当我尝试使用那个api://guid格式时,我会收到错误消息The resource principal named api://guid was not found in the tenant

我还添加了令牌的图像。aud标记与我在Azure中的web api应用程序名称匹配。而scp列出了我附加到我的范围请求的范围。我只是看不到还有什么可以尝试的。

enter image description here

expose api settings for web api


你应该检查的一件事是令牌中的aud声明是什么?它是你的API应用程序ID URI还是API客户端ID? - juunas
@juunas 在 Azure 门户公开的 API 匹配令牌中声称了 aud 声明。 我已在第二个句子中列出它,然后在结尾附近引用了定义。 - pretzelb
观众必须与jwt.ms中显示的令牌中的内容相同。请在此处检查我的答案:https://dev59.com/qa_2oIgBc1ULPQZF1Qwd#76873146 - Gilbert
5个回答

29
问题在于Web API的配置数据。当他们说“ClientId”时,他们真正想要的是“公开API”选项下显示为“应用程序ID URI”的值。我当时填写的是Web Api应用程序注册的guid。下面是正确的填写方式。

问题在于Web API的配置数据。当他们说ClientId时,他们真正想要的是“公开API”选项下显示为“应用程序ID URI”的值。我当时填写的是Web Api应用程序注册的guid。下面是正确的填写方式。

  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "myportal.onmicrosoft.com",
    "TenantId": "mytenant-guid",
    "ClientId": "https://myportal.onmicrosoft.com/test_core_web_api_spa"
  },

关于API格式的注意事项。 当您直接在Azure中注册应用程序时,公开API的格式似乎是api://app-guid。但如果您首先使用Visual Studio创建应用程序,则格式将默认为类似于https:///myportal.onmicrosoft.com/project-name-in-visual-studio


8
如果我能投一百万票,我就会这样做。我花费了两整天的时间来尝试解决这个问题。财产命名草率不负责任,文档也很差。 - Byron Jones
1
令人惊讶的是,如果您按照“创建ASP.NET Core Web应用程序”向导操作,最终将得到一个appsettings.json文件,在该文件中,"ClientId"配置值填充了ClientId(= ^ GUID),而不是Application ID URI(= ^ URI,请参见上文)。 - Peter Wurzinger
我整天都在看 GUID,把我给搞疯了!谢谢你! - Kye

5

像这里的大多数人一样,我也卡在这个问题上了一段时间,而且文档真的让人失望。

在遵循微软指南时,它会在服务器项目中填充 appsettings 文件,如下所示:

"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "contoso.onmicrosoft.com",
"TenantId": "e86c78e2-8bb4-4c41-aefd-918e0565a45e",
"ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd",
 }

令人沮丧的是,这个修复方法非常简单,只需要在客户端ID之前加上api://,使其与JWT中的受众和AAD中服务器应用程序“暴露API”部分的应用程序ID URI匹配即可。
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "contoso.onmicrosoft.com",
"TenantId": "e86c78e2-8bb4-4c41-aefd-918e0565a45e",
"ClientId": "api://41451fa7-82d9-4673-8fa5-69eff5a761fd",
} 

enter image description here


使用.net core 2.2 + Microsoft.AspNetCore.Authentication.AzureAD.UI时,clientId值需要添加前缀api://。而在.net core 3.1 + microsoft.identity.web中,不需要添加api://前缀即可正常工作。 - Avi
我上面描述的示例是使用Blazor WASM模板在.NET Core 3.1中完成的。关键在于clientId需要与在Azure ADD应用程序API中设置的应用程序ID URI匹配。 - Jon Edwards

2

不要只看下面的代码,请仔细阅读!

在pretzelb所说的基础上,不要过于担心客户端的配置(只要客户端工作正常)。假设您遇到的错误与无效签发者或受众有关,则尝试以下操作。

调试您的API并在客户端尝试连接后的某个位置设置调试点,查看HttpContext - Request - Headers - Values,在那里您将看到您的令牌,因此将其放入jwt io网站中,您应该会找到您的签发者和受众,这可能与您预期的完全不同。此时,更改您的TokenValidationParameters以与您在令牌中发现的内容一致,然后它应该可以工作。

使用.Net Core 3.1和Micorosft.Identity.Web(目前处于预览状态),我将向您展示我的工作方式:

这是我的配置,其中包含虚假的GUID。请勿尝试使用相同的实例,您需要查看客户端发送的令牌中的签发者。

"AzureAD": {
    "Instance": "https://sts.windows.net/",
    "ClientId": "28e36e14-5191-4987-bcdf-982d958de2b3",
    "Domain": "funco.com",
    "TenantId": "744ce43f-a10f-499f-29f3-7je6ef439787"
  }

创建一个模型用于AzureAd配置部分的某个位置:
public class AzureModel
    {
        public String Instance { get; set; }
        public String ClientId { get; set; }
        public String Domain { get; set; }
        public String TenantId { get; set; }
    }
}

然后...
services.AddProtectedWebApi(Configuration, AzureADDefaults.AuthenticationScheme,  AzureADDefaults.JwtBearerAuthenticationScheme);
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
    var azureSettings = Configuration.GetSection(AzureADDefaults.AuthenticationScheme).Get<AzureModel>();
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        ValidateIssuer = true,
        ValidIssuer = $"{azureSettings.Instance}{azureSettings.TenantId}/",
        ValidateAudience = true,
        ValidAudience = $"https://{azureSettings.Domain}/{azureSettings.ClientId}",
        //ValidateLifetime = true,
        //ClockSkew = TimeSpan.Zero
    };

    options.Events = new JwtBearerEvents();

    options.Events.OnTokenValidated = async context =>
    {
            await Task.FromResult(0);
    };
    options.Events.OnAuthenticationFailed = async context =>
    {
        await Task.CompletedTask;
    };
    options.Events.OnMessageReceived = async context =>
    {
        await Task.CompletedTask;
    };
});

0
const tokenRequest2 = {
    scopes: ["test_core_web_api_spa/user_impersonation"]
};

或者

const tokenRequest2 = {
    scopes: ["app-guid/user_impersonation"]
};

当您的应用程序需要请求具有特定权限的访问令牌以访问资源 API 时,请传递包含 API 的应用程序 ID URI 的作用域,格式为 /。

不同资源的一些示例范围值:

Microsoft Graph API: https://graph.microsoft.com/User.Read 自定义 Web API:api://11111111-1111-1111-1111-111111111111/api.read 作用域值的格式因接收访问令牌的资源(API)和其接受的 aud 声明值而异。

仅针对 Microsoft Graph,user.read 范围映射到 https://graph.microsoft.com/User.Read,并且两种范围格式可以互换使用。

某些 Web API(例如 Azure 资源管理器 API (https://management.core.windows.net/))在访问令牌的受众声明(aud)中期望有一个尾随正斜杠('/')。在这种情况下,请将作用域传递为 https://management.core.windows.net//user_impersonation,包括双正斜杠('//')。

其他API可能要求在范围值中不包括方案或主机,并且仅期望应用程序ID(GUID)和范围名称,例如:

11111111-1111-1111-1111-111111111111/api.read

参考:https://learn.microsoft.com/zh-cn/azure/active-directory/develop/msal-acquire-cache-tokens

使用 https://jwt.ms/ 解码您的令牌


0

范围不正确,这就是为什么你得到了受众无效的原因。

范围应该类似于api://web_api_clientId/read

enter image description here


1
没有范围可能是正确的。我已经多次使用过那种应用程序 ID URI。 - juunas
@Tony Ju - 在Azure门户中,我没有看到任何类似于您的范围的内容。无论我在哪里查找,都没有以API://开头的内容。我真的很困惑为什么我们在门户中有如此不同的视图。也许是因为我的帐户是企业帐户? - pretzelb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接