【问题标题】:HTTP API: Communicating the need to authenticate to a third partyHTTP API:向第三方传达身份验证的需要
【发布时间】:2016-09-02 00:00:02
【问题描述】:

我正在开发一个包含第三方集成的 RESTful API。我们将 OAuth 与授权代码流一起使用来针对第三方进行身份验证。用户必须先登录我们的服务,然后再登录第三方,这样我们的应用才能访问第三方。

我们的一些资源需要与第三方交互才能完成(例如GET /third-party-userpic)。如果用户已经登录到该服务,我们将从我们的数据存储中检索访问令牌并使用它来检索用户的图片,很简单!

但是,如果我们不持有该用户对该服务的有效凭据,我们就无法获取该用户的照片。这将在首次使用时发生,并且在凭证过期或被撤销时也可能发生。在这种情况下,我们希望向客户端传达他们需要访问授权 URI 并开始 OAuth 流程。

我和我设计这个的同事讨论了几种可能性,包括:

  • 返回 200 OK 成功,并指示在响应正文中需要或允许身份验证。这是不优雅的,并且不能很好地扩展到不同内容类型的资源,并且要求客户端知道它何时访问可能需要此身份验证方案的资源。
  • 返回 403 Forbidden 以及链接以在标头或正文中进行身份验证。问题是这需要与由于权限问题而生成的403 Forbidden 区分开来,并且感觉与我们希望403 的含义不符。
  • 返回401 Unauthorized。这有同样的问题,要求客户端在用户未登录时将其与我们平台生成的401 分开。还有一个问题是401 Unauthorized 是 HTTP 质询-响应身份验证方案的一部分,但我们没有在这里实施挑战-响应流程。客户端从不接触凭据。
  • 创建一个新的4XX 状态代码以允许客户端轻松地将这种情况与其他可能的故障区分开来。从技术角度来看,这似乎是最干净的,但我们可能不需要制作新的状态代码!

【问题讨论】:

    标签: rest http oauth restful-authentication api-design


    【解决方案1】:

    https://www.rfc-editor.org/rfc/rfc6750 的建议如下:

    • 客户端负责请求访问范围,基本上你的主要功能和第三方功能应该被描述为单独的范围
    • 如果客户端尝试访问资源(在您的情况下是某些第三方功能)而不请求访问它,服务器应该响应 403 响应 insufficient_scope 错误

    insufficient_scope 该请求需要比提供的权限更高的权限 访问令牌。资源服务器应该使用 HTTP 响应 403(禁止)状态码,可以包含“范围” 具有访问受保护对象所需范围的属性 资源。

    WWW-Authenticate: Bearer realm="myprotectedresource", 
         error="insufficient_scope", 
         error_description="Insufficient scope for this resource scopes", scope="SOME_SCOPE"
    

    以下是同一来源的授权流程图

     +--------+                               +---------------+
     |        |--(A)- Authorization Request ->|   Resource    |
     |        |                               |     Owner     |
     |        |<-(B)-- Authorization Grant ---|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(C)-- Authorization Grant -->| Authorization |
     | Client |                               |     Server    |
     |        |<-(D)----- Access Token -------|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(E)----- Access Token ------>|    Resource   |
     |        |                               |     Server    |
     |        |<-(F)--- Protected Resource ---|               |
     +--------+                               +---------------+
    

    【讨论】:

    • 401 Unauthorized 需要WWW-Authenticate 质询。我会使用什么挑战?
    • 我只是快速搜索并想出了一些结果外观,我将使用来自tools.ietf.org/html/rfc6750的更多信息更新答案
    • 阅读更多tools.ietf.org/html/rfc6750后,我认为我的回答很愚蠢,我将尝试总结我在编辑版本中学到的东西。
    【解决方案2】:

    首先:我很难相信显示用户图片是一个关键组成部分,因此我假设用户被谴责为默认头像代表的后果更严重。

    在这种情况下,2xx 类响应代码几乎是不可能的,因为操作尚未成功。虽然服务器端有一种机制失败,但客户端有机会纠正这个问题(通过 OAuth 进行身份验证)。这排除了 5xx 级的状态代码,建议使用 4xx 级。

    正如您正确断言的那样,401 并不完全正确,因为此代码特定于请求的资源。来自RFC 7235, section 3.1

    401(未授权)状态码表示该请求没有被应用,因为它缺少目标资源的有效身份验证凭据

    但是,您的目标资源很好;这是一个麻烦的外部服务。除此之外,401 将带您进入 HTTP 身份验证框架的核心,该框架引入了许多实际问题。

    这个范围内最合适的状态码确实是403。来自RFC 7231, section 6.5.1

    403(Forbidden)状态码表示服务器理解请求但拒绝授权。希望公开请求被禁止的原因的服务器可以在响应负载(如果有)中描述该原因。

    虽然这听起来可能无关紧要,但请考虑一下

    [...] 请求可能因与凭据无关的原因而被禁止。

    RFC 还写道:

    客户端可以使用新的或不同的凭据重复请求。

    因此,此代码很宽松,因为它不是特定于请求的资源,而是很好地考虑了阻止成功操作的其他情况。当您按照Choosing an HTTP Status Code — Stop Making It Hard 上的流程图​​进行操作时,这也是您将得到的响应代码。

    【讨论】:

      【解决方案3】:

      在响应正文中以某种方式指示需要或允许身份验证

      我认为这在很大程度上取决于“必需”与“允许”:否则您能否成功完成请求,而只丢失部分结果?

      如果可以(并且想要),也许是这样的:

      HTTP/1.1 200 OK
      Date: Tue, 10 May 2016 01:08:07 GMT
      Content-Type: application/vnd.whatever+json
      Link: <https://third-party.example/authorize>;
          rel="https://example.com/doc/#authorize-3rd-party"
      Warning: 299 - "part of the content is missing; authorize with third party"
      
      ...content with only the pic missing...
      

      如果您不能(或不想),那么 424 (Failed Dependency)403 (Forbidden)(但请参阅 cmets)和 400 (Bad Request) 听起来都像是合适的状态代码。但是,状态码并不是所有问题的解决方案。在响应负载中细化它们的含义是完全正常的。甚至还有一个新标准——RFC 7807

      HTTP/1.1 400 Bad Request
      Date: Tue, 10 May 2016 01:08:07 GMT
      Content-Type: application/problem+json
      
      {
          "type": "https://example.com/doc/#authorize-3rd-party",
          "title": "You need to authorize with a third party for this.",
          "authorizeAt": "https://third-party.example/authorize"
      }
      

      您对 401(未经授权)和发明自己的状态代码的疑虑是非常正确的。

      【讨论】:

      • 我认为添加 RFC 相关部分的链接不会有什么坏处:tools.ietf.org/html/rfc7231#section-6.5.3
      • @DaSourcerer 实际上,我重新阅读了规范并意识到这里的 403 是错误的。它说“拒绝授权”,还说“服务器认为 [凭据] 不足以授予访问权限。”我改变了答案。
      • 什么?不,你是对的! “客户端可以使用新的或不同的凭据重复请求。但是,由于与凭据无关的原因,可能会禁止请求。”这在这里完全足够了。
      • @DaSourcerer 嗯...我真的很想避免这里的 RFC 注释,所以我再次编辑:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-11
      • 2015-07-13
      相关资源
      最近更新 更多