I saw that OAuth is also recommended for facebook canvas applications, and the code that didn’t work for me tried to use signed_request, but every time I changed the session I had to restart:
class Facebook(object): """Wraps the Facebook specific logic""" def __init__(self, app_id=conf.FACEBOOK_APP_ID, app_secret=conf.FACEBOOK_APP_SECRET): self.app_id = app_id self.app_secret = app_secret self.user_id = None self.access_token = None self.signed_request = {} def api(self, path, params=None, method=u'GET', domain=u'graph'): """Make API calls""" if not params: params = {} params[u'method'] = method if u'access_token' not in params and self.access_token: params[u'access_token'] = self.access_token result = json.loads(urlfetch.fetch( url=u'https://' + domain + u'.facebook.com' + path, payload=urllib.urlencode(params), method=urlfetch.POST, headers={ u'Content-Type': u'application/x-www-form-urlencoded'}) .content) if isinstance(result, dict) and u'error' in result: raise FacebookApiError(result) return result def load_signed_request(self, signed_request): """Load the user state from a signed_request value""" try: sig, payload = signed_request.split(u'.', 1) sig = self.base64_url_decode(sig) data = json.loads(self.base64_url_decode(payload)) expected_sig = hmac.new( self.app_secret, msg=payload, digestmod=hashlib.sha256).digest()
I changed the code above to OAuth serverside for the canvas application, and then I could make the application behave the way I wanted. But if I use OAuth 2.0, do I really need signed_request? If I use OAuth, signed_request seems unnecessary and OAuth can do all this. I significantly changed the init_facebook
function:
def init_facebook(self): facebook = Facebook() user = None # initial facebook request comes in as a POST with a signed_request if 'signed_request' in self.request.POST: fbdata= parse_signed_request(self.request.get('signed_request'), facebookconf.FACEBOOK_APP_SECRET) facebook.signed_request = fbdata facebook.user_id = fbdata.get('user_id') facebook.access_token = fbdata.get('oauth_token') if facebook.user_id: graph = GraphAPI(facebook.access_token) user = graph.get_object("me") #write the access_token to the datastore fbuser = FBUser.get_by_key_name(user["id"]) #logging.debug("fbuser "+fbuser.name) self.user = fbuser if not fbuser: fbuser = FBUser(key_name=str(user["id"]), id=str(user["id"]), name=user["name"], profile_url=user["link"], access_token=facebook.access_token) fbuser.put() elif fbuser.access_token != facebook.access_token: fbuser.access_token = facebook.access_token fbuser.put() # try to load or create a user object if facebook.user_id: logging.debug("loading facebook.user_id") user = FBUser.get_by_key_name(facebook.user_id) if user: # update stored access_token if facebook.access_token and \ facebook.access_token != user.access_token: user.access_token = facebook.access_token user.put() # refresh data if we failed in doing so after a realtime ping if user.dirty: user.refresh_data() # restore stored access_token if necessary if not facebook.access_token: facebook.access_token = user.access_token if not user and facebook.access_token: me = facebook.api('/me', {'fields': _USER_FIELDS}) try: friends = [user['id'] for user in me['friends']['data']] user = FBUser(key_name=facebook.user_id, id=facebook.user_id, friends=friends, access_token=facebook.access_token, name=me['name'], email=me.get('email'), picture=me['picture']) user.put() except KeyError, ex: pass # ignore if can't get the minimum fields self.facebook = facebook self.user = user
Now it uses OAuth instead of changing the method from POST
to GET
, which I never understood why this should be done anyway. I have more code, but maybe you already know enough to tell me if I do it wrong and return to a more basic example.
For example, I had problems logging out of the user, and I had to write my own output handler:
class FBLogoutHandler(webapp2.RequestHandler): def get(self): logging.debug('in fblogout') 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"] logging.debug('setting cookie') self.set_cookie("fbsr_" + facebookconf.FACEBOOK_APP_ID, None, expires=time.time() - 86400) logging.debug('redirecting with token '+str(accessed_token)) self.redirect('https://www.facebook.com/logout.php?next=http://www.facebook.com&access_token=%s' % 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'])
This solution may not be the best, so I wonder what are the alternatives for my canvas application?
thanks