如何在服务器端消费Facebook用户访问令牌?

Sco*_*Lin 64 security authentication facebook facebook-authentication facebook-access-token

前言

我正在开发几个Web服务和一些客户端(Web应用程序,移动设备等),它们将通过HTTP与所述服务进行交互.我目前的工作项目是为产品设计身份验证和授权解决方案.我已决定利用外部身份提供商(如Facebook,Google,Microsoft,Twitter等)进行身份验证.

我正试图解决这个问题,"当我的服务器发出请求时,我怎么知道用户是谁以及我怎么能确定?".下面还有更多问题......

要求

  1. 依靠外部身份来表明我正在与谁打交道('userId'基本上就是我所关心的).
  2. 系统应使用基于令牌的身份验证(而不是cookie或基本身份验证).

    我相信这是扩展多个客户端和服务器同时提供松散耦合的正确选择.

工作流程

根据我对基于令牌的身份验证的阅读和理解,以下是我想象的工作流程.现在让我们在Facebook浏览器上关注Facebook.我的假设是其他外部身份提供者应该具有类似的能力,尽管我还没有确认.

请注意,截至撰写时,我的基础是Facebook登录版2.2

  1. 客户:使用JavaScript SDK启动登录Facebook
  2. Facebook:用户验证和批准应用权限(例如访问用户的公开个人资料)
  3. Facebook:向客户发送响应,其中包含用户的访问令牌,ID和签名请求
  4. 客户端:在浏览器会话中存储用户访问令牌(方便地由SDK处理)
  5. 客户端:通过在授权标头中发送用户的访问令牌+用户的ID(可能在自定义标头中),向我的Web服务请求安全资源
  6. 服务器:从请求标头读取用户访问令牌,并通过向Facebook提供的debug_token图形API发送请求来启动验证
  7. Facebook:使用用户访问令牌信息(包含appId和userId)回复服务器
  8. 服务器:通过将appId与预期(自身已知)和userId与客户端请求上发送的内容进行比较,完成对令牌的验证
  9. 服务器:使用请求的资源响应客户端(假设快乐的授权路径)

我想象对于后续的服务器请求会重复步骤5-9(当用户的访问令牌有效时 - 未过期,从FB端撤销,应用权限更改等)

这是一个帮助您完成这些步骤的图表.请理解这个系统不是单页面应用程序(SPA).提到的Web服务是基本上为客户端提供JSON数据的API端点; 它们不提供HTML/JS/CSS(Web客户端服务器除外).

工作流程图

问题

  1. 首先,根据我的前言和要求,所描述的方法是否有任何明显的差距/陷阱?

  2. 是否正在向Facebook执行出站请求,以根据所需/推荐的客户端请求验证访问令牌(上面的步骤6-8)?

    我至少知道,我必须验证来自客户端请求的访问令牌.但是,我不知道在第一次验证后进行后续验证的推荐方法.如果有典型的模式,我很想听听它们.据我所知,根据我的要求,他们可能依赖于应用程序; 但是,我只是不知道该寻找什么.一旦我有了基本想法,我就会进行尽职调查.

    例如,可能的想法:

    • 在第一次验证完成后散列访问令牌+ userId对,并将其存储在分布式缓存中(可由所有Web服务器访问),其到期时间等于访问令牌.根据客户端的后续请求,对访问令牌+ userId对进行散列并检查其是否存在于缓存中.如果存在,则请求被授权.否则,请访问Facebook图形API以确认访问令牌.我假设如果我使用HTTPS(我会这样),这个策略可能是可行的.但是,性能如何比较?

    • 此StackOverflow问题中接受的答案建议在Facebook用户令牌的第一次验证完成后创建自定义访问令牌.然后,自定义令牌将被发送到客户端以用于后续请求.不过,我想知道这是否比上述解决方案更复杂.这需要实现我自己的身份提供者(我想避免的事情,因为我想首先使用外部身份提供者......).这个建议有什么好处吗?

  3. 是signedRequest字段存在于在上述(步骤提到#3的响应此处),相当于签名的请求参数这里在"游戏画布登录"流?

    它们似乎被暗示为等同,因为前者在文档中与后者相关联.但是,我很惊讶在Web文档的"手动构建登录流程" 页面中没有提到游戏页面上提到的验证策略.

  4. 如果#3的答案为"是",那么解码签名的相同身份确认策略是否可以与服务器端预期使用的策略进行比较?

    从FB文档解码和比较

    我不知道是否这可以利用,而不是把所述debug_token图形API的出站呼叫(步骤#6的上方),以确认访问令牌的建议这里:

    来自FB docs的debug_token图形API

    当然,为了在服务器端进行比较,需要将签名的请求部分与请求一起发送到服务器(上面的步骤#5).除了在不牺牲安全性的情况下的可行性之外,我想知道性能与进行出站呼叫相比如何.

  5. 虽然我正处于这种状态,但在什么情况/出于何种目的,您是否会将用户的访问令牌持久存储到数据库中?我没有看到我需要这样做的场景,但是,我可能会忽视某些事情.我很好奇是一些常见的场景可能会引发一些想法.

谢谢!

Tob*_*obi 23

根据您的描述,我建议使用服务器端登录流程,如中所述

以便令牌已经在您的服务器上,并且不需要从客户端传递.如果您使用的是非加密连接,则可能存在安全风险(例如,对于中间人攻击).

步骤将是:

(1)记录人

您需要在参数中指定要从用户收集的权限scope.只需通过普通链接即可触发请求:

GET https://www.facebook.com/dialog/oauth?
    client_id={app-id}
   &redirect_uri={redirect-uri}
   &response_type=code
   &scope={permission_list}
Run Code Online (Sandbox Code Playgroud)

看到

(2)确认身份

GET https://graph.facebook.com/oauth/access_token?
    client_id={app-id}
   &redirect_uri={redirect-uri}
   &client_secret={app-secret}
   &code={code-parameter}
Run Code Online (Sandbox Code Playgroud)

(3)检查访问令牌

您可以检查您在问题中已经说过的令牌

GET /debug_token?input_token={token-to-inspect}
    &access_token={app-token-or-admin-token}
Run Code Online (Sandbox Code Playgroud)

这应该只在服务器端完成,因为否则你会让你的应用程序访问令牌对最终用户可见(不是一个好主意!).

看到

(4)扩展访问令牌

获得(短期)令牌后,您可以执行调用以扩展令牌,如中所述

如下:

GET /oauth/access_token?grant_type=fb_exchange_token
    &client_id={app-id}
    &client_secret={app-secret}
    &fb_exchange_token={short-lived-token}
Run Code Online (Sandbox Code Playgroud)

(5)存储访问令牌

关于在服务器上存储令牌,FB建议这样做:

(6)处理过期的访问令牌

如果令牌已过期(如果您没有保存到期日期并将其与拨打前的当前时间戳进行比较),则FB不会通知您,如果令牌无效,您可能会收到来自FB的错误消息(最多60天后).错误代码将是190:

{
  "error": {
    "message": "Error validating access token: Session has expired at unix 
                time SOME_TIME. The current unix time is SOME_TIME.", 
    "type": "OAuthException", 
    "code": 190
  }
}
Run Code Online (Sandbox Code Playgroud)

看到

如果访问令牌变为无效,解决方案是让该人再次登录,此时您将再次代表他们进行API调用.您的应用用于新用户的登录流程应确定您需要采用哪种方法.

  • 谢谢花时间回应,托比.请原谅我的简洁,但评论有限.阅读后我的想法:1.几个具体问题没有得到解决.2.您建议的代码流如何用于验证客户端对Web服务的请求?你能解释一下客户端 - 服务器的交互吗?正如你的答案现在所说,其中没有任何内容涉及客户.也许从我原来的问题不清楚,但我觉得你误解了"服务器"实体在工作流程步骤中的作用.我编辑了这个问题以澄清.对此有任何困惑道歉. (4认同)