我们真的需要client_secret来获取PKCE流上的access_token吗?

时间:2020-07-23 15:28:42

标签: oauth oauth-2.0 rails-api doorkeeper pkce

我正在构建2个应用程序;前端和后端。

后端将使用Rails API + Doorkeeper Gem(oauth2提供程序)构建,而前端将使用React Native构建。

当前,我正在使用“客户端凭据授予流程”,目前该功能正常。但是经过一段时间的研究,不应将此流程用于仅用于客户端的应用程序,因为一旦有人反编译该应用程序,它就会公开client_secret

我还阅读了有关“隐式授权流程”的信息,该流程仅需client_id。但是,现在这种流程似乎很旧了吗?

并据此:https://auth0.com/docs/api-auth/which-oauth-flow-to-use#is-the-client-a-single-page-app-

建议在“隐式授予流”上使用“带有PKCE的授权代码授予”。我能够使其工作,但是问题是它仍然需要client_secret才能获得access_token,这应该是这样吗?

这是我正在执行的示例流程:

curl -X POST 'http://localhost:3000/oauth/authorize?client_id=xxxx&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&scope=public&code_challenge=testchallenge&code_challenge_method=plain'

这将给我以下答复:

{
    "status": "redirect",
    "redirect_uri": {
        "action": "show",
        "code": "8quZ-EAiKKG2EKnQiSYs3xeFRCgsIwcTbaWNdjnpyFg"
    }
}

然后,我将使用上面的代码通过以下请求获取访问令牌:

curl -i http://localhost:3000/oauth/token \
  -F grant_type="authorization_code" \
  -F client_id="xxxx" \
  -F client_secret="xxxx" \
  -F code="8quZ-EAiKKG2EKnQiSYs3xeFRCgsIwcTbaWNdjnpyFg" \
  -F redirect_uri="urn:ietf:wg:oauth:2.0:oob" \
  -F code_verifier="testchallenge"

现在是问题所在,为了将code交换为access_token,我仍然需要client_secret。如果两者都只暴露我的client_secret,与“客户证书授予流程”有什么不同?

以上命令将返回以下内容:

{
    "access_token": "nQoorBqLxQH4qFpmlx3mGG6Cd_TfX4d3L3gAGOTwrFs",
    "token_type": "Bearer",
    "expires_in": 7200,
    "scope": "public",
    "created_at": 1595517643
}

如果我未在请求中加入client_secret,则响应如下:

{
    "error": "invalid_client",
    "error_description": "Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method."
}

这是我的问题:

  1. 我们真的需要client_secret来获得PKCE流程上的access_token吗?
  2. 如果只公开client_secret,为什么建议使用“ PKCE Flow”?
  3. 与“客户证书授予流程”(其中公开了client_secret)有何不同?

Doorkeeper PKCE文档:https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-PKCE-flow

3 个答案:

答案 0 :(得分:2)

发明了

使用PKCE的授权代码流,用于客户端无法安全保护机密的设置。因此,当将授权代码流与PKCE结合使用时,您不需要秘密,或者更确切地说,客户秘密将毫无意义。

您似乎遇到的是 Doorkeeper错误(请参阅https://github.com/doorkeeper-gem/doorkeeper/issues/1089)。因此,恐怕在他们修复该问题之前,您将不得不使用一些虚拟客户端机密。

但是,只要Doorkeeper正确地实现了其余PKCE流,这就不会成为问题,因为该流不依赖于任何静态客户端机密,而是使用您已经指出的动态创建的代码验证程序。

我不确定我是否正确理解您正在使用哪种客户端来处理登录。如果是SPA或移动应用程序,则应将授权代码流与PKCE一起使用,但是如果要实现“经典” Web应用程序,其中登录是在某些后端服务中处理的,则应使用普通的授权代码流,并使用客户端密码,例如可以信任后端来保护机密。

顺便说一句,我只是检查了我正在开发的一个项目Angular SPA中的代码,该项目通过Auth0作为身份提供者进行身份验证。我在那里使用带有PKCE的授权代码流,而且我绝对不会将任何客户端机密发送到Auth0,因此,这似乎确实是Doorkeeper的问题。

另一件事:我不知道您提供的请求是否只是示例,而不是使用方法 plain 将代码验证程序转换为代码挑战,我宁愿使用安全的方法例如 S256 ,而不是您在问题中引用的RFC中的建议。

答案 1 :(得分:1)

  1. 我们真的需要client_secret来获取PKCE流上的access_token吗?

这取决于。最初,引入PKCE是为了保护公共客户端(不能保护机密的客户端)。但是在最近的最佳实践中,PKCE成为授权码授予(source)的建议

2.1.1。授权码授予

客户端必须阻止将授权代码注入(重播)到
攻击者的授权响应。 PKCE的使用[RFC7636]
为此被推荐。 OpenID Connect的“ nonce”参数和 ID令牌声明[OpenID]也可以使用。 PKCE挑战还是
OpenID Connect“即刻”必须特定于事务且安全地
绑定到进行交易的客户和用户代理
开始。

注意:尽管到目前为止PKCE被设计为一种保护机制
本机应用程序,此建议适用于各种OAuth客户端,
包括网络应用程序。

  1. 如果只公开client_secret,为什么建议使用“ PKCE Flow”?

简而言之,为避免授权代码重播攻击(spec - introduction)。而且这发生在最终用户的设备内部,而不是数据传输中。对于OAuth 2.0令牌请​​求,必须使用TLS。

  1. 与“ Client Credentials Grant Flow”(它公开了client_secret)有什么不同?

由于令牌请求是通过TLS完成的,因此任何授予都不会公开凭据。

在您的情况下,我认为客户是机密客户(spec - client types)。因此,我建议在授权服务器中检查此方面。

答案 2 :(得分:1)

我正在Rails控制台中创建一个Doorkeeper::Application

Doorkeeper::Application.create :name => 'Test App', :uid => 'xxxx', :secret => 'xxxx', :redirect_uri => 'urn:ietf:wg:oauth:2.0:oob'

似乎我需要将Doorkeeper::Application机密字段设置为false,才能在没有access_token的情况下获得client_secret

因此上面的代码将变为:

Doorkeeper::Application.create :name => 'Test App', :uid => 'xxxx', :secret => 'xxxx', :redirect_uri => 'urn:ietf:wg:oauth:2.0:oob', :confidential => false

我在以下位置找到了解决方案: https://github.com/doorkeeper-gem/doorkeeper/blob/master/spec/requests/flows/authorization_code_spec.rb#L348