如何使用资源所有者凭据授权验证 public 客户端的范围

How to validate scopes for public client using resource owner credentials grant

鉴于 public 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 规范,它可以被省略。

The authorization server MUST:

  • require client authentication for confidential clients or for any client that was issued client credentials (or with other authentication requirements)

问题是如果客户端未知,scope = "openid offline_access" 参数无法验证。在我的理解中,范围是为描述应用程序权限而设计的。

马上就有问题了

如何在省略 client_id 时验证客户端范围(有意且按规范)?

一些推理:

  1. 省略client_id的原因是因为任何人都可以轻松地从 JS 客户端检索它并利用它。

  2. 这种情况下的 CORS 验证几乎增加了零值,因为主机 header 可以手动制作。

TL;DR 跳转到下面的 结论 部分...


Javascript 上存储的 client_id 确实可以很容易地检索到,但是这个标识符不是秘密,规范中明确提到了这一点。

The client identifier is not a secret; it is exposed to the resource owner and MUST NOT be used alone for client authentication.

(来源:RFC 6749, section 2.2

这意味着 在您的 public 客户端 上使用 client_id 是可以接受的,所以现在让我们关注第二个问题...客户端身份验证.

您引用的部分说,机密客户端或任何已颁发凭据的客户端必须要求客户端身份验证。您的应用程序不属于上述任何一种情况,因此 客户端身份验证要求不适用

好的,所以您的客户端不需要(实际上也不能执行)客户端身份验证。这会导致您的场景出现问题,因为您想要验证请求的范围,所以让我们尝试找到一个合规的解决方案...

首先是找到一种方法将 client_id 传递给服务器,因为这是必需的。如果客户端是机密的,这将在 Authorization header 中与其秘密一起传递,但我们不在那种情况下。但是,规范允许省略 client_secret,因此我们仍然使用 HTTP header 来传递客户端标识符。

client_secret: REQUIRED. The client secret. The client MAY omit the parameter if the client secret is an empty string.

(来源:RFC 6749, section 2.3.1

现在,我们在服务器端有 client_id,但我们不能相信它,不是根据规范,因为正如我们已经提到的,我们不能单独使用这个标识符客户端身份验证,即使我们尝试了一些聪明的(也就是很容易在不知情的情况下弄错)身份验证机制,还有:

The authorization server MAY establish a client authentication method with public clients. However, the authorization server MUST NOT rely on public client authentication for the purpose of identifying the client.

(来源:RFC 6749, section 2.3

妈的,这下没戏了! 我无法验证,所以我不能信任客户端身份,所以我无法验证范围。

我听到了,但仍有一些希望。现在让我们关注规范的另一部分。

When client authentication is not possible, the authorization server SHOULD employ other means to validate the client's identity -- for example, by requiring the registration of the client redirection URI or enlisting the resource owner to confirm identity.

(来源:RFC 6749, section 10.1

JACKPOT!你确实在客户端身份确认过程中招募了资源所有者,他妈的他把用户名和密码给了客户端,这样就解决了。


结论

您似乎确实有办法完成手头的任务,并且仍然声称符合规范(至少从 OAuth 2.0 方面来看)。唯一要做的就是这只是我的意见,所以我还想给你一个资源所有者端点的实际实现的例子。

如果您转到 Auth0 Authentication API - Resource Owner Endpoint,您会找到具体的实现。看到别人如何处理这个问题总是好的。我已经注意到这个实现选择允许 client_id 请求 body 本身并且有一些其他自定义参数作为选项,但这很好,因为 OAuth 2.0 没有定义严格的协议,只是定义基础。

我想提请您注意的最后一件事是,offline_access 范围在 O​​penID Connect 中用于表示您需要刷新令牌,但您不应该为 [=85= 】 客户。存储刷新令牌(通常是 long-lived 凭证)几乎与存储实际密码一样糟糕。有关此类令牌的一些一般指导,请参阅 refresh tokens

在这种流程中可以针对每个客户端验证范围的建议是不正确的。由于 client_id 是非机密标识符,因此它没有任何价值。任何人都可以从您的 Javascript 实现中获取它并冒充客户端,对用户凭据进行网络钓鱼。因此,请求的范围只能以通用的、非客户端特定的方式处理,而且:

OAuth 2.0 规范建议不要在这种情况下使用资源所有者密码凭证流程,https://www.rfc-editor.org/rfc/rfc6749#section-1.3.3:

The credentials should only be used when there is a high degree of trust between the resource owner and the client (e.g., the client is part of the device operating system or a highly privileged
application), and when other authorization grant types are not
available (such as an authorization code).

在浏览器内 Javascript 的情况下,资源所有者和客户端之间不能高度信任,因为无法可靠地识别客户端。