为什么我在 IdentityServer4 的同意屏幕上看到重复的范围?
Why am I seeing duplicate Scopes on IdentityServer4's consent screen?
我正在编写一个 IdentityServer4 实现并使用 here 中描述的 Quickstart 项目。
当您定义一个 ApiResource(现在使用 InMemory 类)时,IdentityServer 会创建一个与资源同名的范围。例如
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api", "My API")
};
}
将创建一个名为 "api" 的范围(这是在 ApiResource 构造函数中完成的)。如果我在我的客户端对象上添加 "api" 作为允许的范围(使用 InMemoryClients 进行概念验证)并在我的 [=49= 的身份验证请求中的范围查询字符串参数中请求此 api 范围] 客户端我收到一条 invalid_scope 错误消息。
我通过关注 this documentation 发现您可以通过作用域 属性 将作用域添加到 ApiResource,就像这样
new ApiResource
{
Name = "api",
DisplayName = "Custom API",
Scopes = new List<Scope>
{
new Scope("api.read"),
new Scope("api.write")
}
}
所以现在,如果我改为像这样定义我的 ApiResource 并请求范围 api.read 和 api.write(并将它们添加到客户端对象上的 AllowedScopes 属性),那么一切正常很好,除了显示重复范围的同意页面。它显示 api.read 2 次和 api.write 2 次。在此处查看同意屏幕
客户端配置如下:
new Client
{
ClientId = "client.implicit",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:3000/health-check" },
PostLogoutRedirectUris = { "http://localhost:3000" },
AllowedCorsOrigins = { "http://localhost:3000" },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"customApi.read", "customApi.write"
}
}
为什么会这样?我做错了什么吗?
更新:
此处显示范围的发现文档的一部分仅列出一次...
看起来问题出在快速入门 UI... 或 Scope.cs
class 上,具体取决于您如何看待它。具体来说,在 class ConsentService.cs
所示的方法和行中
以下代码
vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
没有过滤掉重复项。也就是说,即使两个作用域具有相同的名称,它们也不被视为相等。因此,如果 GetHashCode
和 Equals
在 Scope.cs
中被覆盖(在 IdentityServer4 中——而不是快速入门),那么它会解决这个问题。在那种情况下,SelectMany 将 return 一个唯一的集合。这是因为 ApiResources 属性 是作为 HashSet 实现的。或者,您可以编写自己的逻辑,使 return 成为一组独特的范围。这就是我解决问题的方法。我在 this post 中写了一些与 Jon Skeet 的回答非常相似的内容,过滤掉了重复的范围。
有一个刚刚在 1.5 中修复的错误解决了这个问题:https://github.com/IdentityServer/IdentityServer4/pull/1030。请升级并查看是否可以为您解决问题。谢谢
问题出在 InMemoryResourcesStore.FindApiResourcesByScopeAsync
实现中的 IdentityService4 代码中,已通过此 commit 修复。您可以使用自 2017 年 6 月 22 日以来包含它的 dev
分支,但它从未在任何针对 .NET Standard 1.4 的 NuGET 包中发布,这非常烦人。
我创建了一个问题并请求对其进行修补:
https://github.com/IdentityServer/IdentityServer4/issues/1470
为了修复视图,我将标有 Todo 的行添加到 ConsentService.cs
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any()))
{
// TODO: Hotfix to cleanup scope duplication:
resources.ApiResources = resources.ApiResources.DistinctBy(p => p.Name).ToList();
return CreateConsentViewModel(model, returnUrl, request, client, resources);
}
这解决了显示问题,但范围仍将多次包含在访问令牌中,这使其更大,因为它使 API 的范围计数平方。我有 3 个作用域,所以每个作用域被包含 3 次,添加了 6 个不需要的作用域副本。但至少它在修复之前是可用的。
我正在编写一个 IdentityServer4 实现并使用 here 中描述的 Quickstart 项目。
当您定义一个 ApiResource(现在使用 InMemory 类)时,IdentityServer 会创建一个与资源同名的范围。例如
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api", "My API")
};
}
将创建一个名为 "api" 的范围(这是在 ApiResource 构造函数中完成的)。如果我在我的客户端对象上添加 "api" 作为允许的范围(使用 InMemoryClients 进行概念验证)并在我的 [=49= 的身份验证请求中的范围查询字符串参数中请求此 api 范围] 客户端我收到一条 invalid_scope 错误消息。
我通过关注 this documentation 发现您可以通过作用域 属性 将作用域添加到 ApiResource,就像这样
new ApiResource
{
Name = "api",
DisplayName = "Custom API",
Scopes = new List<Scope>
{
new Scope("api.read"),
new Scope("api.write")
}
}
所以现在,如果我改为像这样定义我的 ApiResource 并请求范围 api.read 和 api.write(并将它们添加到客户端对象上的 AllowedScopes 属性),那么一切正常很好,除了显示重复范围的同意页面。它显示 api.read 2 次和 api.write 2 次。在此处查看同意屏幕
客户端配置如下:
new Client
{
ClientId = "client.implicit",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:3000/health-check" },
PostLogoutRedirectUris = { "http://localhost:3000" },
AllowedCorsOrigins = { "http://localhost:3000" },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"customApi.read", "customApi.write"
}
}
为什么会这样?我做错了什么吗?
更新: 此处显示范围的发现文档的一部分仅列出一次...
看起来问题出在快速入门 UI... 或 Scope.cs
class 上,具体取决于您如何看待它。具体来说,在 class ConsentService.cs
以下代码
vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
没有过滤掉重复项。也就是说,即使两个作用域具有相同的名称,它们也不被视为相等。因此,如果 GetHashCode
和 Equals
在 Scope.cs
中被覆盖(在 IdentityServer4 中——而不是快速入门),那么它会解决这个问题。在那种情况下,SelectMany 将 return 一个唯一的集合。这是因为 ApiResources 属性 是作为 HashSet 实现的。或者,您可以编写自己的逻辑,使 return 成为一组独特的范围。这就是我解决问题的方法。我在 this post 中写了一些与 Jon Skeet 的回答非常相似的内容,过滤掉了重复的范围。
有一个刚刚在 1.5 中修复的错误解决了这个问题:https://github.com/IdentityServer/IdentityServer4/pull/1030。请升级并查看是否可以为您解决问题。谢谢
问题出在 InMemoryResourcesStore.FindApiResourcesByScopeAsync
实现中的 IdentityService4 代码中,已通过此 commit 修复。您可以使用自 2017 年 6 月 22 日以来包含它的 dev
分支,但它从未在任何针对 .NET Standard 1.4 的 NuGET 包中发布,这非常烦人。
我创建了一个问题并请求对其进行修补: https://github.com/IdentityServer/IdentityServer4/issues/1470
为了修复视图,我将标有 Todo 的行添加到 ConsentService.cs
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any()))
{
// TODO: Hotfix to cleanup scope duplication:
resources.ApiResources = resources.ApiResources.DistinctBy(p => p.Name).ToList();
return CreateConsentViewModel(model, returnUrl, request, client, resources);
}
这解决了显示问题,但范围仍将多次包含在访问令牌中,这使其更大,因为它使 API 的范围计数平方。我有 3 个作用域,所以每个作用域被包含 3 次,添加了 6 个不需要的作用域副本。但至少它在修复之前是可用的。