如何在 IdentityServer 3 中将声明范围和持久化到不同的客户端?

How to scope and persist claims to different clients in IdentityServer 3?

我是 IdentityServer3 的新手,有多个 MVC 客户端,其中用户的声明可能会发生冲突并提供不需要的授权。

这是一个示例,其中有两个客户端能够向用户发送电子邮件和通知。用户可能有权访问这两个应用程序,但应该只能在应用程序 A 中接收通知。我们如何防止用户在应用程序 B 中接收通知?

Application A
Claim Type: ApplicationFunctionality Claim Value:
RecieveNotifications
Claim Type: ApplicationFunctionality Claim Value: RecieveEmails

Application B
Claim Type: ApplicationFunctionality Claim Value: RecieveEmails

合理的解决方案是使用 IUserService 接口在 class 中实现某种逻辑吗?

以我上面描述的方式跨多个客户端使用声明是否正确,我们有时会为跨客户端功能重用声明。我想这需要我为声明命名空间(可能使用客户端发送到 IdentityServer 的请求范围名称),以便区分不同客户端的声明并防止客户端之间未经授权的访问。

这是用户声明 types/claim 值的示例:

Name: John Doe
Email: john.doe@acme.com
PreferedLanguages: English,Swedish,Spanish
ApplicationFunctionality: ClientA.RecieveEmails
ApplicationFunctionality: ClientB.RecieveEmails
ApplicationFunctionality: ClientA.RecieveNotifications
ApplicationFunctionality: ClientB.RecieveNotifications
ApplicationFunctionality: ClientA.ViewBackorders
ApplicationFunctionality: ClientA.DeleteBackorder
ApplicationFunctionality: ClientB.SearchProductInformation
CompanyID: 1145
CompanyID: 6785
CompanyName: Acme Inc
ApplicationLicense: ClientA.PayingNormalUser
ApplicationLicense: ClientB.FreeUser

Company Acme Inc 的一位用户有多个 CompanyID,用于在数据层过滤我们从 webservices\databases 请求的数据。用户可以访问多个应用程序,其中 he/she 可能具有不同级别的功能,具体取决于他们在应用程序中购买的许可证。某些功能存在于多个客户端中,但这并不意味着用户有权在所有客户端中使用相同的功能 he/she 有权访问。

我希望能提供一些关于索赔的指南,或者为我指出一些关于该主题的好资源。我读到声明主要用于与身份相关的信息(电子邮件、姓名、部门、​​最喜欢的颜色、鞋码等),但如果没有 role\permissions 样式声明应该与声明一起使用,那么关于用户的信息应该如何被授权在客户端中做持久化以及如何在 webservices/databases(资源提供者)中过滤数据以便用户只能看到数据 he/she 被授权可以看到?

我的第一个想法是 id_token 和访问令牌使用起来很方便,因为它们是由 STS (IdentityServer) 颁发的,然后保存在 cookie 中。 STS 首先需要在包含用户身份相关信息的 Active Directory 中执行用户帐户查找,并在自定义数据库中查找(使用 Active Directory 用户帐户的用户名)包含有关 role\permissions 和声明的信息用户。

如果不使用 IdentityServer 提供的 cookie 持久令牌,我应该如何保留用户的 roles/permissions 和声明?

客户端(应用程序)和用户可以有自己的声明集。看起来您想拥有可以访问不同资源的应用程序。这是您应该使用范围的地方。基本上定义两个范围来定义对该资源的操作(这是一种常见的做法),即一个用于 'reading' 电子邮件,一个用于 'writing' 电子邮件(例如 emails.reademails.write) 随后其他作用域可以是 backorders.readbackorders.delete。在这里保持一致的命名是一种很好的做法。

好的,既然我们已经定义了这两个范围,您现在可以定义两个客户端,一个只有 emails.read 范围,另一个具有读取和写入范围。所有这一切意味着一个客户端可以访问比另一个客户端更多的资源。

所有用户身份声明都应坚持用户本身。 Application/client specific 永远不应与用户相关联。 NameEmailApplicationLicensePreferredLanguages 都是用户的有效声明,因为它们描述了用户本身以及可以对他们断言的内容。

对于 "complex" 授权,您可能需要查看 this 示例,了解有关如何设置复合安全策略或授权的一些​​想法。

听起来您想针对同一声明向不同的客户公开不同的值。这似乎是一件合乎逻辑的事情,特别是如果您正在与不在您控制范围内的客户进行集成,因此无法向他们指示在每个声明中期望什么,或者请求什么范围。一个简单的示例可能是 "roles" 声明 - 您可能希望根据发出请求的应用程序发送不同的值。如果您要加入其他人的企业,可能有多个 OpenID Connect 提供商,您并不总是可以选择范围或声明名称。

我觉得 Nat Sakimura 在 OpenID Connect 常见问题解答视频 https://www.youtube.com/watch?v=Kb56GzQ2pSk(1 分钟 40 秒)中回避了这一点,即一个实体可能想要向不同的客户端公开不同的身份。

在实现方面,我们添加了 table 和 [identityId, clientId, attributeName, attributeValue] 以允许我们为不同的客户端存储相同的身份属性。在我们的例子中,这些身份属性成为传出 JWT 中的声明。由于关于用户的大多数属性都是全局的(即不是特定于客户端的),我们将此 table 中的数据视为对基本集的覆盖,这样可以避免为每个客户端不必要地重复相同的数据。 iUserService.GetProfileDataAsync() 方法可以访问客户端,因此可以根据数据的消费者定制其响应。