15

我有一个类似于此示例的 ASP.NET Core 3.1 项目:在 WPF 桌面应用程序中使用 Microsoft 身份平台登录用户并调用 ASP.NET Core Web API

我使用的是Identity web1.0 版和 Azure AD,单租户应用程序。

我编辑了清单添加appRoles,因为我只请求应用程序令牌,而不是用户令牌:

[... more json ...]
"appId": "<guid>",
"appRoles": [
    {
        "allowedMemberTypes": [
            "Application"
        ],
        "description": "Accesses the application.",
        "displayName": "access_as_application",
        "id": "<unique guid>",
        "isEnabled": true,
        "lang": null,
        "origin": "Application",
        "value": "access_as_application"
    }
],
"oauth2AllowUrlPathMatching": false,
[... more json ...]

我还启用了idtyp访问令牌声明,以指定这是一个应用程序令牌。:

[... more json ...]
"optionalClaims": {
    "idToken": [],
    "accessToken": [
        {
            "name": "idtyp",
            "source": null,
            "essential": false,
            "additionalProperties": []
        }
    ],
    "saml2Token": []
[... more json ...]

以下请求是通过 Postman 提出的。请注意与范围有关的使用,它在与客户端凭据授予流程/.default相关的文档中提到。

POST /{tenant_id}/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded

scope=api%3A%2F%2{client_id}%2F.default
&client_id={client_id}
&grant_type=client_credentials
&client_secret={secret_key}

该请求返回一个access_token可以使用jwt.ms查看的内容,如下所示,出于安全原因,实际数据已被占位符替换。:

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "[...]",
  "kid": "[...]"
}.{
  "aud": "api://<client_id>",
  "iss": "https://sts.windows.net/<tenant_id>/",
  "iat": 1601803439,
  "nbf": 1601803439,
  "exp": 1601807339,
  "aio": "[...]==",
  "appid": "<app id>",
  "appidacr": "1",
  "idp": "https://sts.windows.net/<tenant_id>/",
  "idtyp": "app",
  "oid": "<guid>",
  "rh": "[..].",
  "roles": [
    "access_as_application"
  ],
  "sub": "<guid>",
  "tid": "<guid>",
  "uti": "[...]",
  "ver": "1.0"
}

我注意到上面的令牌不包括scp. 这似乎是正确的,因为这是应用程序令牌而不是用户令牌。相反,它包含“角色”,作为应用程序令牌的适用。

现在access_token可以在 Postman Get 中用作承载:

GET /api/myapi
Host: https://localhost:5001
Authorization: Bearer {access_token}

对此请求的响应是500 internal error。即有什么问题。看起来像一个正确的access_token应用程序令牌,所以错误似乎在 ASP.NET Core 3.1 控制器端。

ASP.NET 核心 3.1。托管自定义 API 的项目,startup.cs其中包含以下代码:

services.AddMicrosoftIdentityWebApiAuthentication(Configuration);

// This is added for the sole purpose to highlight the origin of the exception.
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;
    
    options.Events.OnTokenValidated = async context =>
    {
        if (context.Principal.Claims.All(x => x.Type != ClaimConstants.Scope)
            && context.Principal.Claims.All(y => y.Type != ClaimConstants.Scp)
            && context.Principal.Claims.All(y => y.Type != ClaimConstants.Roles))
        {
            // This where the exception originates from:
            throw new UnauthorizedAccessException("Neither scope or roles claim was found in the bearer token.");
        }
    };
});

appsettings.json项目包括:

"AzureAD": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "mydomain.onmicrosoft.com",
    "ClientId": "<client_id>",
    "TenantId": "<tenant_id>",
    "Audience": "api://<client_id>"
},

...控制器看起来像这样:

[Authorize]
[Route("api/[controller]")]
public class MyApiController : Controller
{
    [HttpGet]
    public async Task<string> Get()
    {
        return "Hello world!";
    }
}

的根本原因500 internal error是抛出了这个异常:IDW10201: Neither scope or roles claim was found in the bearer token.异常。

更新:

(有关更多详细信息,请参阅下面的答案)。

这段关于“使用 Microsoft 标识平台在您的应用程序中实现授权 - 2020 年 6 月”的视频表明缺少的部分是JwtSecurityTokenHandler.DefaultMapInboundClaims = false;需要设置的这个标志startup.cs- 例如:

public void ConfigureServices(IServiceCollection services)
{
    // By default, the claims mapping will map clain names in the old format to accommodate older SAML applications.
    //'http://schemas.microsodt.com/ws/2008/06/identity/clains/role' instead of 'roles'
    // This flag ensures that the ClaimsIdentity claims collection will be build from the claims in the token
    JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
    
    [...more code...]

4

5 回答 5

12

如果您计划不使用内置范围或角色,这可能会有所帮助。您可以使用下面的 Azure B2C 示例启用“访问控制列表”身份验证。这里有一些官方文档的链接。

https://github.com/AzureAD/microsoft-identity-web/wiki/web-apis#user-content-web-apis-called-by-daemon-apps-using-client-credential-flow

https://docs.microsoft.com/en-us/dotnet/api/microsoft.identity.web.microsoftidentityoptions.allowwebapitobeauthorizedbyacl?view=azure-dotnet-preview

将以下内容添加到您的 AD 配置中: "AllowWebApiToBeAuthorizedByACL": true

例子:

"AzureAdB2C": {
    "Instance": "https://xxx.b2clogin.com/",
    "ClientId": "xxxx",
    "Domain": "xxx.onmicrosoft.com",
    "SignUpSignInPolicyId": "xxx",
    "AllowWebApiToBeAuthorizedByACL": true
  },

ACL/访问控制列表的含义:ACL:https ://en.wikipedia.org/wiki/Access-control_list

于 2020-11-11T14:04:42.147 回答
1

视频“使用 Microsoft 标识平台在您的应用程序中实现授权 - 2020 年 6 月”概述了缺少的部分是JwtSecurityTokenHandler.DefaultMapInboundClaims = false;需要设置的这个标志startup.cs- 例如:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMicrosoftIdentityWebApiAuthentication(Configuration);

    // By default, the claims mapping will map claim names in the old format to accommodate older SAML applications.
    //'http://schemas.microsodt.com/ws/2008/06/identity/clains/role' instead of 'roles'
    // This flag ensures that the ClaimsIdentity claims collection will be build from the claims in the token
    JwtSecurityTokenHandler.DefaultMapInboundClaims = false;


    // Notice that this part is different in the video, 
    // however in this context the following seems to be 
    // the correct way of setting the RoleClaimType:
    services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        // The claim in the Jwt token where App roles are available.
        options.TokenValidationParameters.RoleClaimType = "roles";
    });

    [... more code ...]
}

备选方案 1

也可以像这样为整个应用程序设置授权startup.cs


services.AddControllers(options =>
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireClaim("roles", "access_as_application")
        .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
});

备选方案 2

也可以使用这样的策略:

services.AddAuthorization(config =>
{
    config.AddPolicy("Role", policy => 
        policy.RequireClaim("roles", "access_as_application"));
});

现在可以在控制器请求上使用此策略,如下所示:

[HttpGet]
[Authorize(Policy = "Role")]
public async Task<string> Get()
{
    return "Hello world!";
}

文档中的更多内容:基于策略的角色检查

于 2020-10-04T15:02:08.910 回答
1

只需将 DefaultMapInboundClaims 添加到您的 API 服务配置中

public void ConfigureServices(IServiceCollection services)
{
    JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
}
于 2020-10-04T15:36:19.257 回答
1

当我收到此错误“IDW10202”时,是因为控制器中的这行代码。

HttpContext.ValidateAppRole("MyAppRole");

(这是 Google 返回的唯一结果,因此将此评论放在这里是为了任何人的利益。如果有点偏离主题,请致歉。)

于 2021-11-07T02:15:59.153 回答
0

原因是您使用默认范围发出请求,该范围scope=api%3A%2F%2{client_id}%2F.default不包括 access_token 中的范围声明,当您在 Azure 门户中公开该 API 时,您应该使用为 ASP.NET Core 3.1 API 项目注册的特定范围。

于 2020-10-04T13:01:58.430 回答