如何在服务器端完全启用Facebook OAuth 2.0?

Nik*_*ntz 8 python google-app-engine facebook facebook-graph-api facebook-oauth

我基本上想要保存给定用户的Facebook ID,以便我可以通过Facebook接收更多资料.理想情况下,我想要一个既不使用javascript也不使用cookie的解决方案,只是服务器端,但没有示例,只是方向,所以我把我们可以讨论的内容放在一起.当我将用户链接到我的网站的OAuth对话框时,这是我认为有用的代码:

https://www.facebook.com/dialog/oauth?client_id=164355773607006&redirect_uri=http://www.kewlbusiness.com/oauth

然后我像这样处理它以获取userdata:

class OAuthHandler(webapp2.RequestHandler):
    def get(self):
      args = dict(
        code = self.request.get('code'),
        client_id = facebookconf.FACEBOOK_APP_ID,
        client_secret = facebookconf.FACEBOOK_APP_SECRET,
        redirect_uri = 'http://www.koolbusiness.com/oauth',
      )
      file = urllib.urlopen("https://graph.facebook.com/oauth/access_token?" + urllib.urlencode(args))
      try:
        token_response = file.read()
      finally:
        file.close()
      access_token = cgi.parse_qs(token_response)["access_token"][-1]
      graph = facebook.GraphAPI(access_token)
      user = graph.get_object("me")   
      self.response.out.write(user["id"])
      self.response.out.write(user["name"])
Run Code Online (Sandbox Code Playgroud)

所以有了这个我可以"登录Facebook"为我的网站没有很多杂乱的javascript和我们不想要的cookie.我想为我的网站启用"使用Facebook登录".它应该没有cookie和没有javascript工作,但他们试图让你做的第一件事是Javascript和cookie.所以我已经制定了一个似乎没有cookie而且没有javascript的解决方案,只有OAuth 2.0:你能谈谈我的"解决方案"吗?我正在寻找的实际用途是启用简单的功能,FB用户在我的网站上做了什么,并承认Facebook帐户以标准化的方式登录.

我只是认为它必须工作没有JavaScript SDK和没有cookie,似乎是这种情况.你能说出这个"解决方案"的优点和缺点是什么吗?我认为它比Javascript + Cookie好得多,所以当最小的例子似乎是100%服务器端时,他们为什么欺骗我们使用javascript和cookie?

谢谢

更新 它似乎正确,行为正确,我也可以使用userdata与数据库,并将我的FB名称渲染到首页没有javascript和没有cookie,只是python:

class FBUser(db.Model):
    id = db.StringProperty(required=True)
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    name = db.StringProperty(required=True)
    profile_url = db.StringProperty()
    access_token = db.StringProperty(required=True)
    name = db.StringProperty(required=True)
    picture = db.StringProperty()
    email = db.StringProperty()
    friends = db.StringListProperty()
    dirty = db.BooleanProperty()

class I18NPage(I18NHandler):

    def get(self):
    if self.request.get('code'):
          args = dict(
            code = self.request.get('code'),
            client_id = facebookconf.FACEBOOK_APP_ID,
            client_secret = facebookconf.FACEBOOK_APP_SECRET,
            redirect_uri = 'http://www.kewlbusiness.com/',
          )
      logging.debug("client_id"+str(args))
          file = urllib.urlopen("https://graph.facebook.com/oauth/access_token?" + urllib.urlencode(args))
          try:
        logging.debug("reading file")
            token_response = file.read()
        logging.debug("read file"+str(token_response))
          finally:
            file.close()
          access_token = cgi.parse_qs(token_response)["access_token"][-1]
          graph = main.GraphAPI(access_token)
          user = graph.get_object("me")   #write the access_token to the datastore
      fbuser = main.FBUser.get_by_key_name(user["id"])
          logging.debug("fbuser "+str(fbuser))

          if not fbuser:
            fbuser = main.FBUser(key_name=str(user["id"]),
                                id=str(user["id"]),
                                name=user["name"],
                                profile_url=user["link"],
                                access_token=access_token)
            fbuser.put()
          elif fbuser.access_token != access_token:
            fbuser.access_token = access_token
            fbuser.put()
           self.render_jinja(
                'home_jinja',request=self.request,fbuser=user,...
Run Code Online (Sandbox Code Playgroud)

拥有Facebook用户的变量fbuser和谷歌用户的变量用户现在承认我使用Facebook为我的网站没有错误的杂乱不必要的javascript + cookie.

现在,我可以从我的网站渲染和查看我的Facebook名称,这很棒,它最终的工作方式应该是独立于javascript并且不需要cookie.

当服务器端OAuth 2.0是最干净的解决方案时,为什么文档会推荐javascript + cookie?您是否同意这是最佳解决方案,因为它不依赖于您是否使用javascript或cookie?

更新 可能重复的问题,当我现在已经看到其他人无法注销服务器代码,他们不得不求助于Javascript SDK和一个要求可能是它必须和应该没有JavaScript的工作,所以我做了一些调试,发现"清除"cookie,我不得不更改cookie的名称,虽然它有效但我希望你的评论和/或测试你认为我的项目如何解决这个问题.像这样的注销链接应该可以工作,但事实并非如此.如果我退出两次就可以了,这就是为什么这是一个奇怪的错误,因为我可以解决它,但我仍然不知道是什么让logout需要第二次命中:

https://www.facebook.com/logout.php?next=http://www.myappengineproject.com&access_token=AAACVewZBArF4BACUDwnDap5OrQQ5dx0jsHEKPJkIJJ8GdXlYdni5K50xKw6s8BSIDZCpKBtVWF9maHMoJeF9ZCRRYM1zgZD

看起来我并不是唯一一个试图完全避免使用OAuth 2.0服务器端的解决方案的人.人们可以做任何事情,但他们无法注销:

Facebook Oauth退出

OAuth 2.0与Facebook的官方文档说:

您可以通过将用户指向以下URL来将用户从Facebook会话中注销:

https://www.facebook.com/logout.php?next=YOUR_URL&access_token=ACCESS_TOKEN

YOUR_URL必须是您的站点域中的URL,如Developer App中所定义.

我想做所有服务器端,我发现建议的链接方式离开cookie,以便注销链接不起作用: https://www.facebook.com/logout.php?next=http://{{host}}&access_token={{current_user.access_token}} 它确实重定向,但它没有记录用户我们的网站.它似乎是一个Heisenbug,因为这改变了我,并没有太多的文件.无论如何我似乎能够通过操纵cookie的处理程序来实现功能,这样实际上用户就会被注销:

class LogoutHandler(webapp2.RequestHandler):
    def get(self):
        self.set_cookie("fbsr_" + facebookconf.FACEBOOK_APP_ID, None, expires=time.time() - 86400)
        self.redirect("/")
    def set_cookie(self, name, value, expires=None):

        if value is None:
            value = 'deleted'
            expires = datetime.timedelta(minutes=-50000)
        jar = Cookie.SimpleCookie()
        jar[name] = value
        jar[name]['path'] = '/'
        if expires:
            if isinstance(expires, datetime.timedelta):
                expires = datetime.datetime.now() + expires
            if isinstance(expires, datetime.datetime):
                expires = expires.strftime('%a, %d %b %Y %H:%M:%S')
            jar[name]['expires'] = expires
        self.response.headers.add_header(*jar.output().split(': ', 1))
Run Code Online (Sandbox Code Playgroud)

因此,将处理程序映射到/ auth/logout并将其设置为链接可以有效地将用户从我的站点中注销(无需将用户登录到facebook,希望并且未经测试)

我的代码的其他一些部分是处理Outh通信的OAuth令牌和cookie查找:

def get(self):
    fbuser=None
    profile = None
    access_token = None
    accessed_token = None
    logout = False
    if self.request.get('code'):
      args = dict(
        code = self.request.get('code'),
        client_id = facebookconf.FACEBOOK_APP_ID,
        client_secret = facebookconf.FACEBOOK_APP_SECRET,
        redirect_uri = 'http://self.get_host()/',
      )
      file = urllib.urlopen("https://graph.facebook.com/oauth/access_token?" + urllib.urlencode(args))
      try:
        token_response = file.read()
      finally:
        file.close()
      access_token = cgi.parse_qs(token_response)["access_token"][-1]
      graph = main.GraphAPI(access_token)
      user = graph.get_object("me")   #write the access_token to the datastore
      fbuser = main.FBUser.get_by_key_name(user["id"])
      logging.debug("fbuser "+fbuser.name)

      if not fbuser:
        fbuser = main.FBUser(key_name=str(user["id"]),
                            id=str(user["id"]),
                            name=user["name"],
                            profile_url=user["link"],
                            access_token=access_token)
        fbuser.put()
      elif fbuser.access_token != access_token:
        fbuser.access_token = access_token
        fbuser.put()

    current_user = main.get_user_from_cookie(self.request.cookies, facebookconf.FACEBOOK_APP_ID, facebookconf.FACEBOOK_APP_SECRET)
    if current_user:
      graph = main.GraphAPI(current_user["access_token"])
      profile = graph.get_object("me")
      accessed_token = current_user["access_token"]
Run Code Online (Sandbox Code Playgroud)

我没有创建loginhandler,因为登录基本上是我的根请求处理程序上面的代码.我的用户类如下:

class FBUser(db.Model):
    id = db.StringProperty(required=True)
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    name = db.StringProperty(required=True)
    profile_url = db.StringProperty()
    access_token = db.StringProperty(required=True)
    name = db.StringProperty(required=True)
    picture = db.StringProperty()
    email = db.StringProperty()
Run Code Online (Sandbox Code Playgroud)

我嘲笑了两个基本的提供者 在此输入图像描述 我为Facebook用户使用变量current_user,为google用户使用变量用户,为登录用户使用变量fbuser,因此没有cookie匹配.

我使用的cookie查找代码如下,我想我理解它并且它做了我想要的:

def get_user_from_cookie(cookies, app_id, app_secret):
    """Parses the cookie set by the official Facebook JavaScript SDK.

    cookies should be a dictionary-like object mapping cookie names to
    cookie values.

    If the user is logged in via Facebook, we return a dictionary with the
    keys "uid" and "access_token". The former is the user's Facebook ID,
    and the latter can be used to make authenticated requests to the Graph API.
    If the user is not logged in, we return None.

    Download the official Facebook JavaScript SDK at
    http://github.com/facebook/connect-js/. Read more about Facebook
    authentication at http://developers.facebook.com/docs/authentication/.
    """
    logging.debug('getting user by cookie')
    cookie = cookies.get("fbsr_" + app_id, "")
    if not cookie:
        logging.debug('no cookie found')
        return None
    logging.debug('cookie found')
    response = parse_signed_request(cookie, app_secret)
    if not response:
        logging.debug('returning none')
        return None

    args = dict(
        code = response['code'],
        client_id = app_id,
        client_secret = app_secret,
        redirect_uri = '',
    )

    file = urllib.urlopen("https://graph.facebook.com/oauth/access_token?" + urllib.urlencode(args))
    try:
        token_response = file.read()
    finally:
        file.close()

    access_token = cgi.parse_qs(token_response)["access_token"][-1]
    logging.debug('returning cookie')
    return dict(
        uid = response["user_id"],
        access_token = access_token,
    )
Run Code Online (Sandbox Code Playgroud)

我必须学习cookie来解决这个问题,我希望你能更多地评论.输出欢迎消息需要3个变量,一个用于谷歌用户,一个用于登录facebook用户,变量fbuser用于答案中提到的案例:

当您尝试对新用户进行身份验证时,会使用JavaScript/Cookie.这在您的数据库中不存在,并且您没有他的accessToken.

所以我不得不使用3个变量,也许你只能用2个变量来做?

    {% if user or current_user or fbuser %}
        <div id="user-ident">
            <span>{% trans %}Welcome,{% endtrans %} <b>{{ current_user.name }}{% if not current_user and user %}{{ user.nickname() }}{% endif %}{% if not current_user and not user and fbuser %}{{ fbuser.name }}{% endif %}</span>
        </div>
        {% endif %}
Run Code Online (Sandbox Code Playgroud)

Igo*_*nov 1

当您尝试验证新用户身份时,会使用 JavaScript/Cookie。你的数据库中不存在这些,并且你没有他的accessToken。

当您拥有用户的 accessToken 时 - 如果您愿意,您可以通过 HTTP 从任何编程语言访问 Facebook API,无需 cookies/javascript。有Python客户端,例如: http: //github.com/pythonforfacebook/facebook-sdk(原为https://github.com/facebook/python-sdk