授权策略始终返回403(禁止) - MVC / API

时间:2018-05-14 11:58:31

标签: azure asp.net-core asp.net-identity jwt

我使用端点创建了一个API(带有EF Core的.Net Core 2)来检索某些角色。我将ASPNetIdentity集成到我的项目中,我正在使用AspNetRoles和AspNetRoleClaims。

在调用API时,用户在我的案例中具有特定角色(Admin)。此角色有一些角色声明。在startup.cs中,我添加了此角色的策略:

   options.AddPolicy(
      "Create Roles", policy => policy.RequireClaim("Can create roles", "role.create"));
   options.AddPolicy(
      "View Roles", policy => policy.RequireClaim("Can read roles", "role.read"));
   options.AddPolicy(
      "Edit Roles", policy => policy.RequireClaim("Can update roles", "role.update"));
   options.AddPolicy(
      "Delete Roles", policy => policy.RequireClaim("Can delete roles", "role.delete"));

在我的前端,用户可以使用他们的Microsoft(azure)帐户登录,并且他们的oidc声明(ID)与AspNetUser表中的ID匹配,如果在用户表中找不到他们的oidc,则会自动添加(与他们的oidc id作为aspnetuser id)并且他们获得默认角色。

但是,在调用Role端点时,它总是返回403错误(禁止)。检查表时,用户具有访问端点的正确角色和角色声明。如何继续返回403?

端点如下所示:

[HttpGet]
[Authorize(Policy = "View Roles")]
public IEnumerable<IdentityRole> GetRole()
{
     return _context.Roles;
}

经过一些研究后,我发现了一个帖子,告诉你需要在发送给API的令牌中包含用户的角色(声明),尽管这意味着我需要一个首先返回的GET端点用户的角色,前端需要拿起它并将其添加到令牌,然后使用令牌中包含的角色调用所有其他端点?或者我在这里走错了路?

---- ---- UPDATE

我90%确定策略/授权检查需要将Role声明包含在用户的Token中。然而,这个过程现在如下:

  1. 用户前往前端项目(反应前端)。
  2. 前端使用adal.js检查用户是否经过身份验证,如果未经过身份验证,则会将用户重定向到Microsoft登录页面。
  3. 成功登录后,将调用API。
  4. 在API的DI(AddJwtBearer)中,将oid声明与ASpNetUsers表的ID进行比较,如果不存在,则使用oid值将用户添加到AspNetUser表中。 AspNetUser。
  5. 现在用户也被添加到AspNetUser表中,我可以使用Asp.Net Identity使用Roles和RoleClaims进行授权。

    然而问题是,API初始接收的令牌是Azure令牌,它对我的​​身份表一无所知(如果我错了,请纠正我)。我相信这也是我的政策不起作用的原因(如果我错了,请再次纠正我。)

    我找到了一个问题或多或少相同的帖子(https://joonasw.net/view/adding-custom-claims-aspnet-core-2),诀窍是用我所需的身份声明(例如ClaimTypes.Role)扩展当前令牌。

    我的代码实现如下:

    // Add authentication (Azure AD)
                services
                    .AddAuthentication(sharedOptions =>
                    {
                        sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                        sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 
                        sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; // Use JWT as ChallendgeSchema, if not, ASPNet Identity will be used by default and this will return a default non-existing endpoint (because it is not created): Account/Login; https://stackoverflow.com/questions/45878166/asp-net-core-2-0-disable-automatic-challenge
                    })
                    .AddJwtBearer(options =>
                    {
                        options.Audience = this.Configuration["AzureAd:ClientId"];
                        options.Authority = $"{this.Configuration["AzureAd:Instance"]}{this.Configuration["AzureAd:TenantId"]}";
    
                        // Added events which checks if the user (token user) exists in our own database, if not then the user is being added with a 'User' role
                        options.Events = new JwtBearerEvents()
                        {
                            OnTokenValidated = context =>
                            {
                                // Check if roles are present
                                CheckRoles cr = new CheckRoles();
                                cr.CreateRoles(services.BuildServiceProvider());
    
                                // Check if the user has an OID claim(oid = object id = user id)
                                if (!context.Principal.HasClaim(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier")) 
                                {
                                    context.Fail($"The claim 'oid' is not present in the token.");
                                }
    
                                ClaimsPrincipal userPrincipal = context.Principal;
    
                                CheckUser cu = new CheckUser();
    
                                cu.CreateUser(userPrincipal, services.BuildServiceProvider());
    
                                // Extend the current token with my (test) Role claim
                                var claims = new List<Claim>
                                {
                                    new Claim(ClaimTypes.Role, "Admin")
                                };
                                var appIdentity = new ClaimsIdentity(claims);
                                context.Principal.AddIdentity(appIdentity);
    
    
                                return Task.CompletedTask;
                            }
                        };
                    });
    

    遗憾的是,上述方法不起作用,从前端调用API时,令牌保持不变,并且没有添加RoleClaim ..任何人都知道如何将我的RoleClaim添加到令牌中以便我可以使用我的策略?

1 个答案:

答案 0 :(得分:3)

  

在调用API时,用户在我的案例中具有特定角色(Admin)。   这个角色有一些角色主张。

如果用户在主体对象中有role.read作为ClaimTypes.Role,那么您可以在Startup.cs -

中创建如下所示的政策
public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddAuthorization(options =>
    {
        options.AddPolicy("View Roles", policyBuilder =>
        {
            policyBuilder.RequireAuthenticatedUser()
                .RequireAssertion(context =>
                    context.User.HasClaim(ClaimTypes.Role, "role.read"))
                .Build();
        });
    });
    ...
}

更新

您需要将JwtBearerDefaults.AuthenticationScheme身份验证类型添加到声明标识中,以使其与默认方案匹配。

services
   .AddAuthentication(sharedOptions =>
   {
      ...
   })
   .AddJwtBearer(options =>
   {
      ...
      options.Events = new JwtBearerEvents()
      {         
         OnTokenValidated = context =>
         {
            ...
            var appIdentity = new ClaimsIdentity(claims, 
                   JwtBearerDefaults.AuthenticationScheme);
                               ^^^^^

            context.Principal.AddIdentity(appIdentity);

            return Task.CompletedTask;
         }
      };
   });