Openid 如何从userinfo端点获取声明而不将其包含在id令牌中

Openid 如何从userinfo端点获取声明而不将其包含在id令牌中,openid,identityserver4,openid-connect,Openid,Identityserver4,Openid Connect,我在试图找出如何正确使用userinfo端点时遇到了问题。我的示例使用identity server 4作为授权服务器 假设我有一个js应用程序,它显示经过身份验证的用户的位置。让我们假设我有一些用户存储,它提供包括用户位置在内的声明。iprofileservice接口已经实现,因此GetProfileDataAsync可以从用户存储中为用户检索用户声明 js应用程序需要能够访问用户的位置声明。一种方法是在ids中添加一个identityresource,例如 newidentityresour

我在试图找出如何正确使用userinfo端点时遇到了问题。我的示例使用identity server 4作为授权服务器

假设我有一个js应用程序,它显示经过身份验证的用户的位置。让我们假设我有一些用户存储,它提供包括用户位置在内的声明。iprofileservice接口已经实现,因此GetProfileDataAsync可以从用户存储中为用户检索用户声明

js应用程序需要能够访问用户的位置声明。一种方法是在ids中添加一个identityresource,例如

newidentityresource(“test”,new[]{“location”})

然后将作用域添加到js客户端,例如

new Client
{
    ClientId = "js",
    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        "test"
    }
}
然后配置oidc库以请求该范围,例如

var config = {
   authority: "http://localhost:5000",
   client_id: "js",
   redirect_uri: "http://localhost:5003/callback.html",
   response_type: "id_token token",
   scope:"openid test",
   post_logout_redirect_uri : "http://localhost:5003/index.html",
};
这样做意味着id_令牌将包含位置声明,并且可以作为用户配置文件的一部分进行访问。当使用身份验证期间接收的访问令牌调用userinfo端点时,将返回相同的声明

但是,我反复阅读了()的内容,您应该在标识令牌中放入尽可能少的声明,然后使用userinfo端点检索其他声明。链接文章似乎暗示默认情况下identity server中可以使用此行为

因此,为了做到这一点,使用上面提到的示例代码,我将从请求的oidc配置范围中删除“test”范围。这意味着id令牌将不再填充位置声明。但是,当调用userinfo端点时,“test”作用域不在访问令牌中,因此位置声明不会放在响应中

基本上,我的问题是,您应该如何要求从id令牌中省略声明,但从userinfo端点返回声明


oidc规范似乎还暗示您应该能够使用“claims”请求参数请求特定的声明,但我找不到任何关于identity server(或auth0)的文档.

我实现这一点的一种方法是使用
IdentityResource
ApiResource
进行对比

例如,在启动>配置服务中

    services.AddIdentityServer(x =>
        {
            x.IssuerUri = webServerSettings.Host;
        })
        .AddSigningCredential(webServerSettings.CertificateSubjectDn)
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryIdentityResources(Config.GetIdentityResources());
然后在我的配置中,对于
GetApiResources
,有如下内容:

public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource
                {
                    Name = "MyApi",
                    ApiSecrets = { new Secret("somesecret".Sha256()) },
                    UserClaims = {
                        JwtClaimTypes.GivenName,
                        JwtClaimTypes.FamilyName,
                        JwtClaimTypes.PreferredUserName
                    },
                    Description = "some description",
                    DisplayName = "my api display name",
                    Enabled = true,
                    Scopes = { new Scope("MyApiScope") }
                },

                new ApiResource("otherAPI", "Some other API"),
            };
        }
public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            // The identity scope defines the claims available at the client by calling the 
            // userinfo endpoint, and does not need to match the claims available to the API
            // which are defined as part of the ApiResource above
            new IdentityResource
            {
                Name = "MyApiIdentityScope",
                UserClaims = {
                    JwtClaimTypes.Email,
                    JwtClaimTypes.EmailVerified,
                    JwtClaimTypes.PhoneNumber,
                    JwtClaimTypes.PhoneNumberVerified,
                    JwtClaimTypes.GivenName,
                    JwtClaimTypes.FamilyName,
                    JwtClaimTypes.PreferredUserName
                }
            }
        };
    }

通过这样做,对
GetProfileDataAsync
的调用将具有不同的
RequestedClaimTypes
,这取决于调用是通过UserInfo端点进行的,还是仅仅来自对令牌的请求。

如果仅请求标识令牌,则所有声明都将位于该令牌中。如果您同时请求id_令牌和令牌,则id_令牌中将只包含基本声明,并且可以从userinfo端点检索所有其他声明

这是规范建议的


事实证明,这在我们的生产环境中是按预期工作的,但当我在本地测试时,我总是在id令牌中以及从userinfo端点获得声明。