如何验证每个用户可以使用 OAuth 和 OpenID Connect 访问哪些资源?
How to verify which resources each user can access with OAuth and OpenID Connect?
假设我们有一些 RESTful API 我们想要公开其资源。最终用户将通过客户端应用程序(例如移动应用程序)和基于 Javascript 的客户端 运行 在 Web 浏览器上使用此 API。
对于 OAuth 2.0,此 RESTful API 将位于资源服务器上,我们将拥有一个授权服务器,客户端应用程序将在其上注册。然后,用户将在授权服务器上注册,并能够代表他们或不代表他们授予这些应用程序访问资源的权限。
因此,当用户访问一个客户端应用程序时,他将被重定向到授权服务器并被提示向所述客户端应用程序授予权限。之后发出访问令牌,客户端可以向资源服务器发出请求。
这一切我都很清楚。只缺少一个部分:每个资源的保护可能是 user-dependent。更准确地说,它可能是 claims-dependent。我的意思是我们可以有以下情况:
资源 http://resourceserver.com/api/first-resource 只能供声明为 "ExampleClaim" 且值为 123 的用户访问。
资源 http://resourceserver.com/api/second-resource 只能供声明为 "AnotherClaim" 且值为 123 的用户访问。
任何用户都应该可以访问资源 http://resourceserver.com/api/third-resource。
当我第一次听说 OAuth 正在处理 ASP.NET WebAPI 时,我是这样处理的:当请求是用 Authorization: Bearer [token]
header,在服务器端设置了线程主体,我认为这意味着用户已通过 API 进行身份验证。所以我使用 [Authorize]
属性来验证用户是否可以访问资源。
在更深入地研究了 OAuth 之后,我发现这是对协议的严重滥用。据我了解,OAuth 授权应用程序而非用户。当使用授权 header 发出请求时,据我所知,访问令牌不应包含有关用户的信息,而应包含允许发出请求的应用程序的信息。
考虑到这一点,随请求发送授权 header 不会识别用户,也不会说明用户是否可以访问所述资源。
那么,如何进行这种授权呢?我的意思是,不是执行请求的客户端应用程序的授权,而是用户根据他的声明访问资源的授权?我相信这就是 OpenID Connect 及其 ID 令牌发挥作用的地方,但我不确定。如何做到这一点?
访问令牌不包含用户的声明,但它包含已向客户端应用程序授予权限的用户的主题。 “主题”是一个技术术语,它意味着一个唯一的标识符。简单地说,“主题”是您数据库中的用户 ID。
在受保护的资源端点,您将执行:
- 从请求中提取访问令牌。 (RFC 6750)
- 从授权服务器获取有关访问令牌的详细信息。 (RFC 7662)
- 验证访问令牌。验证包括 (a) 访问令牌是否已过期,以及 (b) 访问令牌是否涵盖受保护资源端点所需的范围(权限)。
上述步骤 1 到 3 是针对 客户端应用程序 的访问控制。 OAuth 2.0(RFC 6749) is for this. See "Protected Resource" by Authlete(由我)了解有关这些步骤的详细信息。
完成上述步骤后,您将做:
- 从访问令牌中提取主题。同样,“主题”是用户的唯一标识符。
- 从您的数据库中检索用户声明。
- 根据需要验证声明。
上述步骤 4 到 6 是针对 用户 的访问控制。 OAuth 2.0 不适用于此。
OpenID Connect is to get an ID token in a verifiable manner. You can confirm that an ID token has been issued by the right party by verifying the signature attached to the ID token. See JSON Web Signature (JWS) (RFC 7515 的主要目的)以获取有关签名的详细信息。
ID 令牌本身并不是保护 Web API 的技术。但是,如果您在 ID 令牌中正确使用 at_hash
声明(请参阅“3.1.3.6. ID Token" in OpenID Connect Core 1.0”),您也许可以将其用于此目的。但是,在受保护的资源端点,它会更容易获得直接从您的数据库声明而不是解析 ID 令牌。
**[评论的附加答案 #1]**
在您的用例中,您不需要 ID 令牌。这是因为访问令牌已经包含有关用户主题的信息。一般情况下,该信息等同于ID token中sub
claim的值。
因此,您不需要 ID 令牌来获取用户的主题。看步骤4的描述,可以找到“Extract the subject from the access token”。
**[评论的附加答案#2]**
So is there anything wrong in extracting the subject from the access token like that and verify the claims? Or this is the right way of doing things?
没有错。例如,假设您定义了一个 Web API、https://api.example.com/profile
,其中 returns 一个用户的个人资料信息。在正常情况下,这样的 API 会接受访问令牌,然后从访问令牌中提取主题以确定引用哪个用户。另一方面,如果 API 没有从访问令牌中提取主题,则它必须要求“主题”作为请求参数来确定要引用哪个用户(或需要包含“子”声明)。即使在这种情况下,API 也必须检查请求参数指定的主题和与访问令牌关联的主题是否相同,否则会成为安全问题。
提取主题后检查声明也是正常步骤。例如,您可能希望根据用户已付费的计划(免费计划、轻量计划、企业计划等)限制服务的功能。在这种情况下,您必须参考 plan
声明。当然,只有在从访问令牌中提取主题后才能检查这样的声明。
因此,(1) 从访问令牌中提取主题,然后 (2) 验证用户的声明是正常的,甚至是受保护资源端点实现中的典型步骤。
你说得对,OAuth 是 NOT an authentication protocol 而不是委托协议。
OpenID Connect 向 OAuth 2.0 的令牌发行模型添加了两个值得注意的身份构造。
身份令牌 - 从一方到另一方的交付
可以启用联合身份 SSO 用户体验
标准化身份属性API - 客户端可以
检索给定用户所需的身份属性。
可以将 ID 令牌提交给 userinfo_endpoint 以获取信息并提供用户已通过 OpenID 提供者身份验证的保证级别。
顺便说一句:"sub" 即仅在授权服务器的上下文中是唯一的。建议如果您存储 sub,您还存储类似 iss-sub 的内容。想法是 Google 上的 tsmith 可能不是 Twitter
上的 tsmith
假设我们有一些 RESTful API 我们想要公开其资源。最终用户将通过客户端应用程序(例如移动应用程序)和基于 Javascript 的客户端 运行 在 Web 浏览器上使用此 API。
对于 OAuth 2.0,此 RESTful API 将位于资源服务器上,我们将拥有一个授权服务器,客户端应用程序将在其上注册。然后,用户将在授权服务器上注册,并能够代表他们或不代表他们授予这些应用程序访问资源的权限。
因此,当用户访问一个客户端应用程序时,他将被重定向到授权服务器并被提示向所述客户端应用程序授予权限。之后发出访问令牌,客户端可以向资源服务器发出请求。
这一切我都很清楚。只缺少一个部分:每个资源的保护可能是 user-dependent。更准确地说,它可能是 claims-dependent。我的意思是我们可以有以下情况:
资源 http://resourceserver.com/api/first-resource 只能供声明为 "ExampleClaim" 且值为 123 的用户访问。
资源 http://resourceserver.com/api/second-resource 只能供声明为 "AnotherClaim" 且值为 123 的用户访问。
任何用户都应该可以访问资源 http://resourceserver.com/api/third-resource。
当我第一次听说 OAuth 正在处理 ASP.NET WebAPI 时,我是这样处理的:当请求是用 Authorization: Bearer [token]
header,在服务器端设置了线程主体,我认为这意味着用户已通过 API 进行身份验证。所以我使用 [Authorize]
属性来验证用户是否可以访问资源。
在更深入地研究了 OAuth 之后,我发现这是对协议的严重滥用。据我了解,OAuth 授权应用程序而非用户。当使用授权 header 发出请求时,据我所知,访问令牌不应包含有关用户的信息,而应包含允许发出请求的应用程序的信息。
考虑到这一点,随请求发送授权 header 不会识别用户,也不会说明用户是否可以访问所述资源。
那么,如何进行这种授权呢?我的意思是,不是执行请求的客户端应用程序的授权,而是用户根据他的声明访问资源的授权?我相信这就是 OpenID Connect 及其 ID 令牌发挥作用的地方,但我不确定。如何做到这一点?
访问令牌不包含用户的声明,但它包含已向客户端应用程序授予权限的用户的主题。 “主题”是一个技术术语,它意味着一个唯一的标识符。简单地说,“主题”是您数据库中的用户 ID。
在受保护的资源端点,您将执行:
- 从请求中提取访问令牌。 (RFC 6750)
- 从授权服务器获取有关访问令牌的详细信息。 (RFC 7662)
- 验证访问令牌。验证包括 (a) 访问令牌是否已过期,以及 (b) 访问令牌是否涵盖受保护资源端点所需的范围(权限)。
上述步骤 1 到 3 是针对 客户端应用程序 的访问控制。 OAuth 2.0(RFC 6749) is for this. See "Protected Resource" by Authlete(由我)了解有关这些步骤的详细信息。
完成上述步骤后,您将做:
- 从访问令牌中提取主题。同样,“主题”是用户的唯一标识符。
- 从您的数据库中检索用户声明。
- 根据需要验证声明。
上述步骤 4 到 6 是针对 用户 的访问控制。 OAuth 2.0 不适用于此。
OpenID Connect is to get an ID token in a verifiable manner. You can confirm that an ID token has been issued by the right party by verifying the signature attached to the ID token. See JSON Web Signature (JWS) (RFC 7515 的主要目的)以获取有关签名的详细信息。
ID 令牌本身并不是保护 Web API 的技术。但是,如果您在 ID 令牌中正确使用 at_hash
声明(请参阅“3.1.3.6. ID Token" in OpenID Connect Core 1.0”),您也许可以将其用于此目的。但是,在受保护的资源端点,它会更容易获得直接从您的数据库声明而不是解析 ID 令牌。
**[评论的附加答案 #1]**
在您的用例中,您不需要 ID 令牌。这是因为访问令牌已经包含有关用户主题的信息。一般情况下,该信息等同于ID token中sub
claim的值。
因此,您不需要 ID 令牌来获取用户的主题。看步骤4的描述,可以找到“Extract the subject from the access token”。
**[评论的附加答案#2]**
So is there anything wrong in extracting the subject from the access token like that and verify the claims? Or this is the right way of doing things?
没有错。例如,假设您定义了一个 Web API、https://api.example.com/profile
,其中 returns 一个用户的个人资料信息。在正常情况下,这样的 API 会接受访问令牌,然后从访问令牌中提取主题以确定引用哪个用户。另一方面,如果 API 没有从访问令牌中提取主题,则它必须要求“主题”作为请求参数来确定要引用哪个用户(或需要包含“子”声明)。即使在这种情况下,API 也必须检查请求参数指定的主题和与访问令牌关联的主题是否相同,否则会成为安全问题。
提取主题后检查声明也是正常步骤。例如,您可能希望根据用户已付费的计划(免费计划、轻量计划、企业计划等)限制服务的功能。在这种情况下,您必须参考 plan
声明。当然,只有在从访问令牌中提取主题后才能检查这样的声明。
因此,(1) 从访问令牌中提取主题,然后 (2) 验证用户的声明是正常的,甚至是受保护资源端点实现中的典型步骤。
你说得对,OAuth 是 NOT an authentication protocol 而不是委托协议。
OpenID Connect 向 OAuth 2.0 的令牌发行模型添加了两个值得注意的身份构造。
身份令牌 - 从一方到另一方的交付 可以启用联合身份 SSO 用户体验
标准化身份属性API - 客户端可以
检索给定用户所需的身份属性。
可以将 ID 令牌提交给 userinfo_endpoint 以获取信息并提供用户已通过 OpenID 提供者身份验证的保证级别。
顺便说一句:"sub" 即仅在授权服务器的上下文中是唯一的。建议如果您存储 sub,您还存储类似 iss-sub 的内容。想法是 Google 上的 tsmith 可能不是 Twitter
上的 tsmith