考虑到公共Javascript客户端想要使用资源所有者密码凭据授权类型访问API端点的情况。
典型的请求如下所示:
username = "john.doe@mail.com"
password = "MyP@assw0rd!"
grant_type = "password"
scope = "openid offline_access"
没有传递client_id
,因为客户端无法存储client_secret
,并且根据Resource Owner Password Credentials Grant规范,它可能会被省略。
授权服务器必须:
- 要求机密客户端或任何客户端身份验证 已发布客户端凭据的客户端(或与其他客户端 认证要求)
问题是如果客户端未知,则无法验证scope = "openid offline_access"
参数。在我的理解中,范围是为描述应用程序权限而设计的。
立即出现一个问题。
client_id
(故意和按规格),如何验证客户端范围?一些推理:
省略client_id
的原因是因为任何人都可以轻松地从JS客户端检索它并利用它。
在这种情况下,CORS验证增加了几乎为零的值,因为主机头可以手动制作。
答案 0 :(得分:1)
TL; DR 跳转到下面的结论部分...
确实可以轻松检索存储在Javascript上的client_id
,但这个标识符不是秘密,规范明确提到了这一点。
客户端标识符不是秘密;它暴露给资源所有者,不得单独用于客户端身份验证。
这意味着可以在公共客户端上使用client_id
,所以现在让我们关注第二个问题......客户端身份验证。
您引用的部分指出,机密客户端或已颁发凭据的任何客户端都必须要求客户端身份验证。您的应用程序不属于任何一种情况,因此客户端身份验证要求不适用。
好的,所以您的客户端不需要(也不能实际执行)客户端身份验证。这会导致您的方案出现问题,因为您要验证请求的范围,因此我们尝试找到合规的解决方案......
首先要找到一种方法将client_id
传递给服务器,因为这是必要的。如果客户端是机密的,那么它将在Authorization
标题中传递它的秘密,但我们不是在这种情况下。但是,规范允许省略client_secret
,所以我们仍然使用该HTTP头来传递客户端标识符。
client_secret :必填。客户的秘密。如果客户端密钥是空字符串,则客户端可以省略该参数。
现在,我们在服务器端有client_id
,但我们不能信任它,而不是根据规范,因为正如我们已经提到的,我们不能单独使用此标识符进行客户端身份验证即使我们尝试了一些聪明的人(也很容易在不知情的情况下弄错了)认证机制,还有:
授权服务器可以建立客户端身份验证方法 与公共客户。但是,授权服务器绝不能依赖 公共客户端身份验证,以识别 客户端。
该死的,这没有在哪里! 我无法进行身份验证,因此无法信任客户身份,因此无法验证范围。
我听到了你的意见,但仍有一些希望。让我们现在关注规范的另一部分。
当无法进行客户端身份验证时,授权服务器应该采用其他方法来验证客户端的身份 - 例如,要求注册客户端重定向URI 或征用资源所有者确认身份。
JACKPOT!您确实在客户身份确认过程中登记了资源所有者,他将他的用户名和密码提供给客户端以便解决问题。
您似乎确实有办法完成手头的任务,仍然声称符合规范(至少从OAuth 2.0方面来说)。剩下要做的唯一事情就是这只是我的观点,所以我还想给你一个资源所有者端点的实际实现的例子。
如果你去Auth0 Authentication API - Resource Owner Endpoint,你会找到一个具体的实现。看到其他人如何处理这个问题总是很好。我已经注意到,此实现选择允许client_id
在请求正文本身上并且有一些其他自定义参数作为选项,但这很好,因为OAuth 2.0没有定义严格的协议,只是定义了基础。 / p>
最后一件事,我想引起你的注意,是在OpenID Connect中使用offline_access
范围来表示你想要一个刷新令牌,但你不应该为公共客户端这样做。存储刷新令牌(通常是长期存在的凭证)几乎与存储实际密码一样糟糕。有关这些类型的令牌的一般性指导,请参阅refresh tokens。
答案 1 :(得分:0)
在这样的流程中,每个客户端可以验证范围的建议是不正确的。由于client_id
是非秘密标识符,因此不会添加任何值。任何人都可以从您的Javascript实现中获取它并模拟客户端,用户凭据进行网络钓鱼。因此,请求的范围只能以通用的非客户端特定方式处理,而且:
OAuth 2.0规范建议不要在这种情况下使用资源所有者密码凭据流https://tools.ietf.org/html/rfc6749#section-1.3.3:
凭证只应在高度使用时使用 资源所有者和客户端(例如,客户端)之间的信任 是设备操作系统的一部分或高度特权的 申请),以及其他授权许可类型不是 可用(例如授权码)。
在浏览器内的Javascript案例中,资源所有者和客户之间不能存在高度信任,因为无法可靠地识别客户端。