Identity Server 4:如何仅为 UserInfoEndpoint 添加自定义声明并在 AccessToken 中排除它们?
Identity Server 4: How to add Custom Claims only for UserInfoEndpoint and exclude them in AccessToken?
我们希望在 UserInfoEndPoint 的用户声明中提供大量数据,但我们不想将这些声明嵌入到 AccessToken 中。
据我所知,当我们想要使 AccessToken 大小保持较小时,我们可以 return 来自 UserInfoEndPoint 的附加数据。 Ref: Profile Service
因此,我按照以下方式实现了 IProfileService:
public class ProfileService : IProfileService
{
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
// Get data from Db
var claims = new List<Claim>();
claims.Add(new Claim("global_company_id", "88888888-D964-4A2B-8D56-B893A5BCD700"));
//..... add series of additional claims
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
context.IsActive = true;
}
}
它return是来自 UserInfoEndpoint 的扩展声明。但是,问题是这些声明集也包含在 Jwt 访问令牌中,它使令牌不必要地变得更大。
{
"nbf": 1582236568,
"exp": 1582236868,
"iss": "https://localhost:44378",
"aud": [
"https://localhost:44378/resources",
"testapi"
],
"client_id": "testClientId",
"sub": "78452916-D260-4219-927C-954F4E987E70",
"auth_time": 1582236558,
"idp": "local",
"name": "ttcg",
"global_company_id": "88888888-D964-4A2B-8D56-B893A5BCD700",
//........ series of claims here
"scope": [
"openid",
"profile",
"address",
"roles",
"country",
"customClaims"
],
"amr": [
"pwd"
]
}
这是我在 Identity Server Provider 中的客户端配置:
var clientUrl = "http://localhost:64177";
return new Client
{
ClientName = "Test Web Application",
ClientId = "testClientId",
AllowedGrantTypes = GrantTypes.Hybrid,
AllowOfflineAccess = false,
RequireConsent = false,
RedirectUris = new List<string>
{
$"{clientUrl}/signin-oidc"
},
PostLogoutRedirectUris = new List<string>
{
$"{clientUrl}/signout-callback-oidc"
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.OfflineAccess,
"t1_global_ids"
},
ClientSecrets =
{
new Secret("abc123".Sha256())
}
};
这是我连接到 Identity Server 的 MVC .Net Core 客户端配置
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
options.AccessDeniedPath = "/AccessDenied";
})
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://identityserverUrl";
options.ClientId = "testClientId";
options.ResponseType = "code id_token";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("CustomClaims"); // <-- here
options.SaveTokens = true;
options.ClientSecret = "abc123";
options.GetClaimsFromUserInfoEndpoint = true;
});
你能帮我解决一下吗,让我知道是否可以用令牌隐藏这些声明?
我试过一次并找到了一个解决方案,但它可能被认为是 "hacky" -- 它有效但我从未在生产中使用过它,所以使用风险自负。
您的 ProfileService 的 GetProfileDataAsync() 方法会在不同时间被调用——创建 JWT 时、命中 UserEndpoint 时等。在这种情况下,您不希望在创建时添加自定义声明JWT,因此创建一个条件,当 "Caller" 是 JWT 创建过程(类型为 "ClaimsProviderAccessToken")时不添加它们。
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
if (context.Caller != IdentityServerConstants.ProfileDataCallers.ClaimsProviderAccessToken)
{
var sub = context.Subject.GetSubjectId();
// Get data from Db
var claims = new List<Claim>();
claims.Add(new Claim("global_company_id", "88888888-D964-4A2B-8D56-B893A5BCD700"));
//..... add series of additional claims
context.IssuedClaims = claims;
}
}
有了这个,JWT 不包含您的自定义声明,但是如果您点击 UserEndpoint,它将 return 这些用户的声明作为 JSON 的一部分。
我们希望在 UserInfoEndPoint 的用户声明中提供大量数据,但我们不想将这些声明嵌入到 AccessToken 中。
据我所知,当我们想要使 AccessToken 大小保持较小时,我们可以 return 来自 UserInfoEndPoint 的附加数据。 Ref: Profile Service
因此,我按照以下方式实现了 IProfileService:
public class ProfileService : IProfileService
{
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
// Get data from Db
var claims = new List<Claim>();
claims.Add(new Claim("global_company_id", "88888888-D964-4A2B-8D56-B893A5BCD700"));
//..... add series of additional claims
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
context.IsActive = true;
}
}
它return是来自 UserInfoEndpoint 的扩展声明。但是,问题是这些声明集也包含在 Jwt 访问令牌中,它使令牌不必要地变得更大。
{
"nbf": 1582236568,
"exp": 1582236868,
"iss": "https://localhost:44378",
"aud": [
"https://localhost:44378/resources",
"testapi"
],
"client_id": "testClientId",
"sub": "78452916-D260-4219-927C-954F4E987E70",
"auth_time": 1582236558,
"idp": "local",
"name": "ttcg",
"global_company_id": "88888888-D964-4A2B-8D56-B893A5BCD700",
//........ series of claims here
"scope": [
"openid",
"profile",
"address",
"roles",
"country",
"customClaims"
],
"amr": [
"pwd"
]
}
这是我在 Identity Server Provider 中的客户端配置:
var clientUrl = "http://localhost:64177";
return new Client
{
ClientName = "Test Web Application",
ClientId = "testClientId",
AllowedGrantTypes = GrantTypes.Hybrid,
AllowOfflineAccess = false,
RequireConsent = false,
RedirectUris = new List<string>
{
$"{clientUrl}/signin-oidc"
},
PostLogoutRedirectUris = new List<string>
{
$"{clientUrl}/signout-callback-oidc"
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.OfflineAccess,
"t1_global_ids"
},
ClientSecrets =
{
new Secret("abc123".Sha256())
}
};
这是我连接到 Identity Server 的 MVC .Net Core 客户端配置
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
options.AccessDeniedPath = "/AccessDenied";
})
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://identityserverUrl";
options.ClientId = "testClientId";
options.ResponseType = "code id_token";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("CustomClaims"); // <-- here
options.SaveTokens = true;
options.ClientSecret = "abc123";
options.GetClaimsFromUserInfoEndpoint = true;
});
你能帮我解决一下吗,让我知道是否可以用令牌隐藏这些声明?
我试过一次并找到了一个解决方案,但它可能被认为是 "hacky" -- 它有效但我从未在生产中使用过它,所以使用风险自负。
您的 ProfileService 的 GetProfileDataAsync() 方法会在不同时间被调用——创建 JWT 时、命中 UserEndpoint 时等。在这种情况下,您不希望在创建时添加自定义声明JWT,因此创建一个条件,当 "Caller" 是 JWT 创建过程(类型为 "ClaimsProviderAccessToken")时不添加它们。
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
if (context.Caller != IdentityServerConstants.ProfileDataCallers.ClaimsProviderAccessToken)
{
var sub = context.Subject.GetSubjectId();
// Get data from Db
var claims = new List<Claim>();
claims.Add(new Claim("global_company_id", "88888888-D964-4A2B-8D56-B893A5BCD700"));
//..... add series of additional claims
context.IssuedClaims = claims;
}
}
有了这个,JWT 不包含您的自定义声明,但是如果您点击 UserEndpoint,它将 return 这些用户的声明作为 JSON 的一部分。