在 Identity Server 4 id_token 中包含核心身份角色声明

Adr*_*ian 10 asp.net-identity identityserver4

在成功登录到与 AspNet Core Identity 集成的 Identity Server 4 (IS4) 后,我遇到了一个问题,包括角色声明。这会阻止我使用“Authorize(Roles=xxx)”属性来保护对 api 的访问。

我正在遵循 Identity Server 4/AspNet Identity 集成文档中提供的示例。令人惊讶的是,文档没有包含角色声明的示例,我认为这是一个非常常见的场景。

我根据 IS4 文档使用主机中指定的 HybridClientCredential 授权类型设置了 3 个项目,创建了 AspNet Identity DB,并将角色(“Admin”)手动添加到 EF Core 生成的数据库中。如果我已正确设置,我希望该角色在成功登录后自动包含在用户声明中。

  • AspNet Core(个人身份验证)/身份服务器(主机)
  • MVC 应用程序(客户端)
  • MVC Web API (api)

这是我正在使用的代码:

主持人:

public class Config
{
    public static IEnumerable<ApiResource> GetApiResources()
    {
        return new[]
        {
            // expanded version if more control is needed
            new ApiResource
            {
                Name = "api1",

                Description = "My API",

                // secret for using introspection endpoint
                ApiSecrets =
                {
                    new Secret("secret".Sha256())
                },

                // include the following using claims in access token (in addition to subject id)
                UserClaims = { "role" },

                // this API defines two scopes
                Scopes =
                {
                    new Scope()
                    {
                        Name = "api1",
                        DisplayName = "Full access to API 1",
                        UserClaims = new [] { "role" }
                    }
                }
            }
        };
    }

    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile()
        };
    }

    public static IEnumerable<Client> GetClients()
    {
        return new List<Client>()
        {
            new Client
            {
                ClientId = "mvc",
                ClientName = "MVC Client",
                AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

                RequireConsent = false,

                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },

                RedirectUris           = { "http://localhost:5002/signin-oidc" },
                PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
                AlwaysIncludeUserClaimsInIdToken = true,
                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "api1"
                },
                AllowOfflineAccess = true
            }
        };
    }

}
Run Code Online (Sandbox Code Playgroud)

客户:

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Cookies"
        });

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

        app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
        {
            AuthenticationScheme = "oidc",
            SignInScheme = "Cookies",

            Authority = "http://localhost:5000",
            RequireHttpsMetadata = false,

            ClientId = "mvc",
            ClientSecret = "secret",

            ResponseType = "code id_token",
            Scope = { "api1", "offline_access" },

            GetClaimsFromUserInfoEndpoint = true,
            SaveTokens = true,

            TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = JwtClaimTypes.Name,
                RoleClaimType = JwtClaimTypes.Role,
            }
        });

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

接口:

[Route("api/[controller]")]
[Authorize(Roles="Admin")]
public class ValuesController : Controller
{
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET api/values/5
    [HttpGet("{id}")]
    public string Get(int id)
    {
        return "value";
    }

    // POST api/values
    [HttpPost]
    public void Post([FromBody]string value)
    {
    }

    // PUT api/values/5
    [HttpPut("{id}")]
    public void Put(int id, [FromBody]string value)
    {
    }

    // DELETE api/values/5
    [HttpDelete("{id}")]
    public void Delete(int id)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

除了主机在成功登录后不包括角色声明之外,我的设置工作正常。

我想知道是否有人可以帮助我知道如何解决这个问题?谢谢。

Ang*_*Sky 10

Hi this is the way I create custom Policy role-based,

1)

in Config -> Client section add:

 Claims = new Claim[]
 {
     new Claim("Role", "admin")
 }
Run Code Online (Sandbox Code Playgroud)

2)

Then in the Api -> startup.cs -> ConfigureServices add:

    services.AddAuthorization(options =>
    {
        options.AddPolicy("admin", policyAdmin =>
        {
            policyAdmin.RequireClaim("client_Role", "Admin");
        });

        //otherwise you already have "api1" as scope

        options.AddPolicy("admin", builder =>
        {
            builder.RequireScope("api1");
        });
    });
Run Code Online (Sandbox Code Playgroud)

3)

Then use it this way:

    [Route("api/[controller]")]
    [Authorize("admin")]
    public class ValuesController : Controller
Run Code Online (Sandbox Code Playgroud)

If you analize the token you'll have something like this:

{
  "alg": "RS256",
  "kid": "2f2fcd9bc8c2e54a1f29acf77b2f1d32",
  "typ": "JWT"
}

{
  "nbf": 1513935820,
  "exp": 1513937620,
  "iss": "http://localhost/identityserver",
  "aud": [
    "http://localhost/identityserver/resources",
    "MySecuredApi"
  ],
  "client_id": "adminClient",
  "client_Role": "admin",       <---------------
  "scope": [
    "api.full_access",
    "api.read_only"
  ]
}
Run Code Online (Sandbox Code Playgroud)

PS:

you cannot use "RequireRole" because Identity Server 4 when you use:

Claims = new Claim[]
{
    new Claim("Role", "admin")
},
Run Code Online (Sandbox Code Playgroud)

will create:

client_Role: admin
Run Code Online (Sandbox Code Playgroud)

but "RequireRole" uses:

Role: admin
Run Code Online (Sandbox Code Playgroud)

so it won't match.

As you can test:

using System.Security.Claims;
MessageBox.Show("" + new Claim("Role", "admin"));
Run Code Online (Sandbox Code Playgroud)

UPDATE WITH RequireRole : Clear "ClientClaimsPrefix"

in Config -> Client section add:

ClientClaimsPrefix = "",
Claims = new Claim[]
{
 new Claim(ClaimTypes.Role, "Admin")
}
Run Code Online (Sandbox Code Playgroud)

Then in the Api -> startup.cs -> ConfigureServices add:

services.AddAuthorization(options =>
{
    options.AddPolicy("admin", builder =>
    {
        builder.RequireRole(new[] { "Admin" });
    });
});
Run Code Online (Sandbox Code Playgroud)

Then use it this way:

    [Route("api/[controller]")]
    [Authorize("admin")]
    public class ValuesController : Controller
Run Code Online (Sandbox Code Playgroud)

Otherwise without the "Policy" use like this:

[Authorize(Roles = "Admin")]
Run Code Online (Sandbox Code Playgroud)