【问题标题】:How do you properly grant access to an ApiResource for a given client in Identity Server 4.0.3?在 Identity Server 4.0.3 中,如何正确授予给定客户端对 ApiResource 的访问权限?
【发布时间】:2020-12-04 00:01:53
【问题描述】:

以前我们可以定义以下配置,它会起作用:

public static IEnumerable<ApiScope> GetApiScopes() =>
    new List<ApiScope>
    {
        new ApiScope(
            name: "Scope1",
            displayName: "scope1 description",
            userClaims: new[] { "claim1" }),
        new ApiScope(
            name: "Scope2",
            displayName: "scope2 description",
            userClaims: new[] { "claim2", "claim3", "claim4"}),
        new ApiScope(
            name: "Scope3",
            displayName: "scope3 description",
            userClaims: new[] { "claim5" }),
        new ApiScope(
            name: "Scope4",
            displayName: "scope4 description",
            userClaims: new[] { "claim6" })
    };
    
public static IEnumerable<ApiResource> GetApiResources() =>
    new List<ApiResource>
    {
        new ApiResource("MyApi", "MyApi description")
        {
            ApiSecrets = { new Secret("secret").Sha256() },
            Scopes =
            {
                "Scope1",
                "Scope2",
                "Scope3",
                "Scope4"
            }
        }
    };
    
public static IEnumerable<Client> GetClients() =>
    new List<Client>
    {
        new Client
        {
            Enabled = true,
            ClientId = "client",
            ClientSecrets = "secret"
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
            AllowOfflineAccess = true,
            AccessTokenType = AccessTokenType.Reference,
            RequireConsent = false,
            RequirePkce = false,
            UpdateAccessTokenClaimsOnRefresh = true,
            RefreshTokenExpiration = TokenExpiration.Absolute,
            AbsoluteRefreshTokenLifetime = 123456,
            RefreshTokenUsage = TokenUsage.ReUse,
            AccessTokenLifetime = 600000,
            AllowedScopes = { "MyApi" }, // This previously worked, now it doesn't
        }
    };

但是由于各种变化,有点解释here,你不能再做上面的事情了,因为"MyApi"写在Client.AllowedScopes不是一个范围——也就是说你不能像你一样请求访问资源之前通过提供他们的名字来完成

相反,要使上述内容在 Identity Server 4.0.3 中正常工作,您必须执行以下 hack,我认为这是非常错误的,因此问题如下:

public static IEnumerable<ApiScope> GetApiScopes() =>
    new List<ApiScope>
    {
        new ApiScope(
            name: "Scope1",
            displayName: "scope1 description",
            userClaims: new[] { "claim1" }),
        new ApiScope(
            name: "Scope2",
            displayName: "scope2 description",
            userClaims: new[] { "claim2", "claim3", "claim4"}),
        new ApiScope(
            name: "Scope3",
            displayName: "scope3 description",
            userClaims: new[] { "claim5" }),
        new ApiScope(
            name: "Scope4",
            displayName: "scope4 description",
            userClaims: new[] { "claim6" }),
        // Wrapper
        new ApiScope(
            name: "MyApi",
            displayName: "",
            // Manually add all claims from above scopes. 
                        // If you end up in the future changing one of the above scopes's required claims,
                        // well, make sure you do the same here...
            userClaims: new[] { "claim1", "claim2", "claim3", "claim4", "claim5", "claim6"})        
    };
    
public static IEnumerable<ApiResource> GetApiResources() =>
    new List<ApiResource>
    {
        new ApiResource("MyApi", "MyApi description")
        {
            ApiSecrets = { new Secret("secret").Sha256() },
            Scopes =
            {
                "MyApi"
            }
        }
    };
    
public static IEnumerable<Client> GetClients() =>
    new List<Client>
    {
        new Client
        {
            Enabled = true,
            ClientId = "client",
            ClientSecrets = "secret"
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
            AllowOfflineAccess = true,
            AccessTokenType = AccessTokenType.Reference,
            RequireConsent = false,
            RequirePkce = false,
            UpdateAccessTokenClaimsOnRefresh = true,
            RefreshTokenExpiration = TokenExpiration.Absolute,
            AbsoluteRefreshTokenLifetime = 123456,
            RefreshTokenUsage = TokenUsage.ReUse,
            AccessTokenLifetime = 600000,
                        // now works because we have a fake "MyApi" scope,
                        // encapsulating our previously well-defined structure of scopes
            AllowedScopes = { "MyApi" }, 
        }
    };

将整个ApiResource 的作用域包装到一个作用域中并定义所述作用域中存在的所有声明是零意义的。

有人能说明一下吗 - 实现我们在过去版本的 Identity Server4 中实现的目标的正确方法是什么?

编辑:基本上我想问的是 - 您如何要求为特定资源授予一组特定范围? (如果它们中的任何一个都不存在于令牌中 - 使其无效)

【问题讨论】:

    标签: c# asp.net-core .net-core authorization identityserver4


    【解决方案1】:

    之前这样做的原因是因为在以前的版本中,资源定义自动包含了同名的范围。

    请注意,这里的 MyApi 是作用域名称,等于资源名称。

    AllowedScopes = { "MyApi" }
    

    但是令牌中包含资源名称作为受众,这很令人困惑。因此,如果客户端至少有一个范围是资源的一部分,则它可以访问该资源。在资源内,应验证范围以确保客户端有权使用资源的特定部分,例如:

    services
        .AddAuthorization(options =>
        {
            options.AddPolicy("Scope1", p => p.RequireClaim("scope", "Scope1"));
    

    在您的情况下,客户端似乎可以访问整个资源,而不管指定的范围。

    所以你应该做的是,验证资源中的范围并在客户端定义中命名允许的范围:

    AllowedScopes = { "Scope1", "Scope2", "Scope3", "Scope4" }
    

    如果允许所有范围,则省略该行。这将自动包含所有允许的范围。


    根据 cmets 更新。

    您可以通过以下方式验证在 api 中接收不记名令牌的范围:

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddIdentityServerAuthentication(options =>
        {
            options.JwtBearerEvents = new JwtBearerEvents
            {
                OnTokenValidated = async context =>
                {
                    // To verify if required scopes are included:
                    var requiredScopes = new List<string> { "scope1" , "scope2", "scope3", "scope4" };
                    var foundScopes = context.Principal.Claims.Count(c => c.Type == "scope" && allowedScopes.Contains(c.Value));
    
                    if (foundScopes != requiredScopes.Count)
                    {
                        context.Fail("Invalid number of scopes");
                    }
                }
            };
        });
    

    【讨论】:

    • 谢谢,但您的一些示例,特别是我认为不正确的 AddPolicy 示例。为什么 - 范围不是声明。范围是一些东西,它可以包含与指定范围相关的声明列表。我认为您应该使用 RequireScope 方法和所需范围列表。但这引入了下一个问题。您将如何在全球范围内应用该策略,同时又不妨碍现有已定义的操作/控制器以及对特定策略的定义访问权限?如果您使用DefaultPolicy,它仅适用于您仅提供 [Authorize] 的情况
    • 但是对于提到的其他情况,如果您已经使用 [Authorize(SomePolicy)] 定义了一个操作/端点,则不会使用默认策略。此外,当您要求ApiResource 下列出的所有范围时,即使接受令牌也没有意义。有没有办法使 Jwt/Introspection 身份验证处理程序短路,这样如果所需的范围不存在 - 只需将其视为无效的访问/引用令牌?
    • 请忽略使用RequireScope的部分。似乎您的方法是在 ASP.NET Core 中声明该意图的正确方法。
    • @SpiritBob IdentityServer 将所有范围作为声明(声明类型“范围”)包含在访问令牌中。您可以使用 RequireClaim("scope"),但如果我没记错,那么 RequireScope 是等效的。您可以使用 [Authorize] 来保护属于范围一部分的功能。请注意,资源是一个逻辑名称,不限于每个资源一个 api。当您有多个 api,其中每个 api 实现一个或多个作为资源一部分的范围时,这是有意义的。查看我的回答 here 了解更多详情。
    • @SpiritBob 另外,当我有 client1AllowedScopes = { "Scope1", "Scope2" }client2AllowedScopes = { "Scope3", "Scope4" } 然后两者都可以访问资源,但你不希望 client1 访问 Scope3Scope4 功能,反之亦然,限制 client2 访问 Scope1Scope2 功能。
    猜你喜欢
    • 2018-08-06
    • 1970-01-01
    • 2022-10-21
    • 2013-09-25
    • 2010-09-26
    • 1970-01-01
    • 2020-01-11
    • 2016-08-09
    • 2019-10-03
    相关资源
    最近更新 更多