Current pure python solution for facebook-oauth?
Asked Answered
C

1

8

It looks like I was not the only one trying to avoid javascript altogether for a solution with OAuth 2.0 serverside. People could do everything but they couldn't logout:

Facebook Oauth Logout

Oauth Logout using facebook graph api

Facebook OAuth2 Logout does not remove fb_ cookie

The official documentation for OAuth 2.0 with Facebook says:

You can log a user out of their Facebook session by directing them to the following URL:

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

YOUR_URL must be a URL in your site domain, as defined in the Developer App.

I wanted to do everything serverside and I found that the suggested way to link leaves the cookie so that the logout link doesn't work: https://www.facebook.com/logout.php?next=http://{{host}}&access_token={{current_user.access_token}} It does redirect but it doesn't log the user out of my website. It seemed like a Heisenbug since this was changing to me and there was not much documentation. I anyway seemed to be able to achieve the functionality with a handler that manipulates the cookie so that in effect the user is logged out:

class LogoutHandler(webapp2.RequestHandler):
    def get(self):
        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"] 
        self.set_cookie("fbsr_" + facebookconf.FACEBOOK_APP_ID, None, expires=time.time() - 86400)
        self.redirect("https://www.facebook.com/logout.php?next=http://%s&access_token=%s" get_host(), accessed_token)
    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))
    def get_host(self):
        return os.environ.get('HTTP_HOST', os.environ['SERVER_NAME'])

So mapping the handler to /auth/logout and setting this to the link effectively logs the user out of my site (but I'm not sure about logging the user out of facebook, hopefully and untested)

Some other parts of my code is handling the OAuth tokens and cookie lookups for the Oauth communication:

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 = '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"]

I didn't make a loginhandler since login basically is the code above at my root request handler. My user class is as follows:

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()

I mocked together two basic providers enter image description here And I use the variable current_user for the facebook user and the variable user for the google user and the variable fbuser for a user who is logging in and therefore has no cookie match.

Christ answered 24/11, 2011 at 19:50 Comment(3)
Logging users out of your website is entirely up to you - you just have to delete the session you're using to keep track of their login details. How would an external API do that?Loiret
Also, um, what's the question? You seem to just be describing what you did.Loiret
The default token expiry is 2 hours, unless you request the offline_access permission.Dominique
C
2

I believe you are meant to keep track of authentication on your website for yourself. You have to unset the cookie for your site yourself.

At most, it will invalidate the facebook access token, if it is a session only token and not an offline access token.

Capsize answered 21/2, 2012 at 8:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.