You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IdentityServer ApiResources配置疑问及无效范围问题排查

关于Duende IdentityServer的两个配置疑问

问题背景

我正在遵循最新的Duende IdentityServer官方快速入门教程,遇到两个技术疑问:

疑问1

我的理解是否正确:ApiResource本质是ApiScope和IdentityScope的逻辑分组?比如将3个ApiScope("apiscope1"、"apiscope2"、"apiscope3")封装到名为"api"的ApiResource后,客户端只需指定"api"作为范围,即可自动获取这三个ApiScope?

疑问2

为何在MyApi的OpenIdConnect配置中添加"apiResource"作为请求范围时会出现无效范围错误,移除该范围后登录/登出流程可正常运行?


配置代码

MyApi(客户端)配置

builder.Services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
    {
        var idsvrConfig = builder.Configuration.GetSection("IdentityServer").Get<IdentityServerConfiguration>();

        options.Authority = idsvrConfig.Authority;
        options.ClientId = idsvrConfig.ClientId;
        options.ClientSecret = idsvrConfig.ClientSecret;
        options.ResponseType = idsvrConfig.ResponseType;
        options.Scope.Clear();
        //options.Scope.AddRange(idsvrConfig.Scopes);
        options.Scope.Add("openid");
        options.Scope.Add("profile");
        options.Scope.Add("verification");
        options.Scope.Add("apiResource"); //<-------指定此项时无法正常工作!!

        options.GetClaimsFromUserInfoEndpoint = idsvrConfig.GetClaimsFromUserInfoEndpoint;
        options.SaveTokens = idsvrConfig.SaveTokens;
        foreach (var claims in idsvrConfig.ClaimActionsMapJsonKey)
        {
            options.ClaimActions.MapJsonKey(claims.Key, claims.Value);
        }
    });

IdentityServerApi(授权服务器)配置

var apiResources = new List<ApiResource>()
{
    new ApiResource()
    {
        Name = "apiResource",
        DisplayName ="ApiResource",
        Scopes = new string[] {"test" }
    }
};


var apiScopes = new List<ApiScope>()
{
    new ApiScope()
    {
            Name = "test",
    }
};

var identityResources = new List<IdentityResource>
{
    new IdentityResources.OpenId(),
    new IdentityResources.Profile(),
    new IdentityResource()
    {
        Name = "verification",
        UserClaims = new List<string>
        {
            JwtClaimTypes.Email,
            JwtClaimTypes.EmailVerified
        }
    }
};


var clients = new List<Client>
{
    // interactive ASP.NET Core Web App
    new Client
    {
        ClientId = "api",
        ClientSecrets = { new Secret(){
            Value = "supersecretpass"
        }},

        AllowedGrantTypes = GrantTypes.Code,

        // where to redirect after login
        RedirectUris = { "https://localhost:44330/signin-oidc" },

        // where to redirect after logout
        PostLogoutRedirectUris = { "https://localhost:44330/signout-callback-oidc" },

        AllowedScopes = new List<string>
        {
            IdentityServerConstants.StandardScopes.OpenId,
            IdentityServerConstants.StandardScopes.Profile,
            "verification",
            "apiResource",
            "test"
        }
    }
};


// Add Identity Server services
builder.Services.AddIdentityServer()
    .AddInMemoryClients(clients)
    .AddInMemoryIdentityResources(identityResources)
    .AddInMemoryApiScopes(apiScopes)
    .AddInMemoryApiResources(apiResources)
    //.AddInMemoryClients(builder.Configuration.GetSection("IdentityServer:Clients"))
    //.AddInMemoryIdentityResources(builder.Configuration.GetSection("IdentityServer:IdentityResources"))
    //.AddInMemoryApiScopes(builder.Configuration.GetSection("IdentityServer:ApiScopes"))
    //.AddInMemoryApiResources(builder.Configuration.GetSection("IdentityServer:ApiResources"))
    .AddTestUsers(TestUsers.Users);

环境信息

  • 两个项目均基于.NET 7
  • MyApi使用Microsoft.AspNetCore.Authentication.OpenIdConnect 7.0.5
  • IdentityServer使用Duende.IdentityServer 6.2.3

解答

针对疑问1的回答

你的理解不正确。ApiResource和ApiScope的关系并非“逻辑分组”,而是:

  • ApiScope是客户端请求的最小权限单元,代表客户端可访问API的具体功能/数据范围
  • ApiResource是对受保护API的元数据抽象,用于定义API的名称、显示名、颁发的Claims等,仅用于关联对应的ApiScope
  • 客户端不能直接请求ApiResource的名称作为范围,必须显式请求具体的ApiScope名称

若想实现类似“分组请求”的效果,需在客户端配置中显式列出所有需要的Scope,或通过自定义逻辑在授权服务器端处理范围组合,IdentityServer本身没有原生的范围分组别名功能。

针对疑问2的回答

出现“无效范围”错误的核心原因是:apiResource是ApiResource的名称,并非有效的Scope标识。

在当前配置中,有效范围仅包括:

  • 身份资源范围:openid、profile、verification
  • API范围:test

虽然你在客户端的AllowedScopes中添加了apiResource,但IdentityServer只会识别ApiScope和IdentityResource的名称为有效范围,ApiResource本身不能被当作Scope请求。

解决方法:

  1. 将MyApi配置中的options.Scope.Add("apiResource")替换为options.Scope.Add("test")
  2. 清理客户端AllowedScopes中的无效项,仅保留有效范围名称

另外在Duende IdentityServer v6+版本中,ApiResource的核心作用是配置API的JWT颁发规则(如Claims、签名算法),客户端请求的权限始终以具体的ApiScope为单位。


内容的提问来源于stack exchange,提问作者Style

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 05:37:49