def _exchange_oauth_code_for_access_token(code, redirect_uri=None): """Try to exchange an OAuth `code` for a proper access_token. Following code is based on the PHP API: https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php """ if not code: return None ## Create a default for the redirect_uri: ## - when using the JavaScript SDK the default should be '' ## - for other pages it should be the URL of the current page. if not redirect_uri: redirect_uri = '' ## We need to remove ``signed_request``, ``code`` and ``state`` ## GET parameters from the current URL. redirect_uri = cleanup_oauth_url(redirect_uri) try: logger.info('Trying to exchange the code for an access_token. redirect_uri=%r', redirect_uri) token_response = FacebookAuthorization.convert_code(code, redirect_uri=redirect_uri) expires = token_response.get('expires') access_token = token_response['access_token'] except open_facebook_exceptions.OAuthException, e: ## This sometimes fails, but it shouldn't raise exceptions ## because this happens when an user deauthorizes your ## application and then tries to re-authenticate. logger.warn('Exchange of code %r failed.', unicode(e))
def get_facebook_graph(request=None, access_token=None, redirect_uri=None, raise_=False): ''' given a request from one of these - js authentication flow (signed cookie) - facebook app authentication flow (signed cookie) - facebook oauth redirect (code param in url) - mobile authentication flow (direct access_token) - offline access token stored in user profile returns a graph object redirect path is the path from which you requested the token for some reason facebook needs exactly this uri when converting the code to a token falls back to the current page without code in the request params specify redirect_uri if you are not posting and recieving the code on the same page ''' # this is not a production flow, but very handy for testing if not access_token and request.REQUEST.get('access_token'): access_token = request.REQUEST['access_token'] # should drop query params be included in the open facebook api, # maybe, weird this... from open_facebook import OpenFacebook, FacebookAuthorization from django.core.cache import cache expires = None if hasattr(request, 'facebook') and request.facebook: graph = request.facebook _add_current_user_id(graph, request.user) return graph # parse the signed request if we have it signed_data = None if request: signed_request_string = request.REQUEST.get('signed_data') if signed_request_string: logger.info('Got signed data from facebook') signed_data = parse_signed_request(signed_request_string) if signed_data: logger.info('We were able to parse the signed data') # the easy case, we have an access token in the signed data if signed_data and 'oauth_token' in signed_data: access_token = signed_data['oauth_token'] if not access_token: # easy case, code is in the get code = request.REQUEST.get('code') if code: logger.info('Got code from the request data') if not code: # signed request or cookie leading, base 64 decoding needed cookie_name = 'fbsr_%s' % facebook_settings.FACEBOOK_APP_ID cookie_data = request.COOKIES.get(cookie_name) if cookie_data: signed_request_string = cookie_data if signed_request_string: logger.info('Got signed data from cookie') signed_data = parse_signed_request(signed_request_string) if signed_data: logger.info('Parsed the cookie data') # the javascript api assumes a redirect uri of '' redirect_uri = '' if signed_data: # parsed data can fail because of signing issues if 'oauth_token' in signed_data: logger.info('Got access_token from parsed data') # we already have an active access token in the data access_token = signed_data['oauth_token'] else: logger.info('Got code from parsed data') # no access token, need to use this code to get one code = signed_data.get('code', None) if not access_token: if code: cache_key = hash_key('convert_code_%s' % code) access_token = cache.get(cache_key) if not access_token: # exchange the code for an access token # based on the php api # https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php # create a default for the redirect_uri # when using the javascript sdk the default # should be '' an empty string # for other pages it should be the url if not redirect_uri: redirect_uri = '' # we need to drop signed_data, code and state redirect_uri = cleanup_oauth_url(redirect_uri) try: logger.info( 'trying to convert the code with redirect uri: %s', redirect_uri) # This is realy slow, that's why it's cached token_response = FacebookAuthorization.convert_code( code, redirect_uri=redirect_uri) expires = token_response.get('expires') access_token = token_response['access_token'] # would use cookies instead, but django's cookie setting # is a bit of a mess cache.set(cache_key, access_token, 60 * 60 * 2) except (open_facebook_exceptions.OAuthException, open_facebook_exceptions.ParameterException) as e: # this sometimes fails, but it shouldnt raise because # it happens when users remove your # permissions and then try to reauthenticate logger.warn('Error when trying to convert code %s', unicode(e)) if raise_: raise else: return None elif request.user.is_authenticated(): # support for offline access tokens stored in the users profile profile = try_get_profile(request.user) access_token = get_user_attribute( request.user, profile, 'access_token') if not access_token: if raise_: message = 'Couldnt find an access token in the request or the users profile' raise open_facebook_exceptions.OAuthException(message) else: return None else: if raise_: message = 'Couldnt find an access token in the request or cookies' raise open_facebook_exceptions.OAuthException(message) else: return None graph = OpenFacebook(access_token, signed_data, expires=expires) # add user specific identifiers if request: _add_current_user_id(graph, request.user) return graph
def _test_equal(self, url, output): converted = cleanup_oauth_url(url) self.assertEqual(converted, output)
def get_facebook_graph(request=None, access_token=None, redirect_uri=None, raise_=False): """ given a request from one of these - js authentication flow (signed cookie) - facebook app authentication flow (signed cookie) - facebook oauth redirect (code param in url) - mobile authentication flow (direct access_token) - offline access token stored in user profile returns a graph object redirect path is the path from which you requested the token for some reason facebook needs exactly this uri when converting the code to a token falls back to the current page without code in the request params specify redirect_uri if you are not posting and recieving the code on the same page """ # this is not a production flow, but very handy for testing if not access_token and request.REQUEST.get("access_token"): access_token = request.REQUEST["access_token"] # should drop query params be included in the open facebook api, # maybe, weird this... from open_facebook import OpenFacebook, FacebookAuthorization from django.core.cache import cache parsed_data = None expires = None if hasattr(request, "facebook"): graph = request.facebook _add_current_user_id(graph, request.user) return graph if not access_token: # easy case, code is in the get code = request.REQUEST.get("code") if code: logger.info("Got code from the request data") if not code: # signed request or cookie leading, base 64 decoding needed signed_data = request.REQUEST.get("signed_request") cookie_name = "fbsr_%s" % facebook_settings.FACEBOOK_APP_ID cookie_data = request.COOKIES.get(cookie_name) if cookie_data: signed_data = cookie_data # the javascript api assumes a redirect uri of '' redirect_uri = "" if signed_data: logger.info("Got signed data from facebook") parsed_data = FacebookAuthorization.parse_signed_data(signed_data) if parsed_data: logger.info("Got parsed data from facebook") # parsed data can fail because of signing issues if "oauth_token" in parsed_data: logger.info("Got access_token from parsed data") # we already have an active access token in the data access_token = parsed_data["oauth_token"] else: logger.info("Got code from parsed data") # no access token, need to use this code to get one code = parsed_data.get("code", None) if not access_token: if code: cache_key = "convert_code_%s" % code access_token = cache.get(cache_key) if not access_token: # exchange the code for an access token # based on the php api # https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php # create a default for the redirect_uri # when using the javascript sdk the default # should be '' an empty string # for other pages it should be the url if not redirect_uri: redirect_uri = "" # we need to drop signed_request, code and state redirect_uri = cleanup_oauth_url(redirect_uri) try: logger.info("trying to convert the code with redirect uri: %s", redirect_uri) # This is realy slow, that's why it's cached token_response = FacebookAuthorization.convert_code(code, redirect_uri=redirect_uri) expires = token_response.get("expires") access_token = token_response["access_token"] # would use cookies instead, but django's cookie setting # is a bit of a mess cache.set(cache_key, access_token, 60 * 60 * 2) except open_facebook_exceptions.OAuthException, e: # this sometimes fails, but it shouldnt raise because # it happens when users remove your # permissions and then try to reauthenticate logger.warn("Error when trying to convert code %s", unicode(e)) if raise_: raise else: return None elif request.user.is_authenticated(): # support for offline access tokens stored in the users profile profile = request.user.get_profile() access_token = getattr(profile, "access_token", None) if not access_token: if raise_: message = "Couldnt find an access token in the request or the users profile" raise open_facebook_exceptions.OAuthException(message) else: return None else: if raise_: message = "Couldnt find an access token in the request or cookies" raise open_facebook_exceptions.OAuthException(message) else: return None
def get_facebook_graph(request=None, access_token=None, redirect_uri=None, raise_=False): ''' given a request from one of these - js authentication flow (signed cookie) - facebook app authentication flow (signed cookie) - facebook oauth redirect (code param in url) - mobile authentication flow (direct access_token) - offline access token stored in user profile returns a graph object redirect path is the path from which you requested the token for some reason facebook needs exactly this uri when converting the code to a token falls back to the current page without code in the request params specify redirect_uri if you are not posting and recieving the code on the same page ''' #this is not a production flow, but very handy for testing if not access_token and request.REQUEST.get('access_token'): access_token = request.REQUEST['access_token'] # should drop query params be included in the open facebook api, # maybe, weird this... from open_facebook import OpenFacebook, FacebookAuthorization from django.core.cache import cache parsed_data = None expires = None if hasattr(request, 'facebook'): graph = request.facebook _add_current_user_id(graph, request.user) return graph if not access_token: #easy case, code is in the get code = request.REQUEST.get('code') if code: logger.info('Got code from the request data') if not code: #signed request or cookie leading, base 64 decoding needed signed_data = request.REQUEST.get('signed_request') cookie_name = 'fbsr_%s' % facebook_settings.FACEBOOK_APP_ID cookie_data = request.COOKIES.get(cookie_name) if cookie_data: signed_data = cookie_data #the javascript api assumes a redirect uri of '' redirect_uri = '' if signed_data: logger.info('Got signed data from facebook') parsed_data = FacebookAuthorization.parse_signed_data( signed_data) if parsed_data: logger.info('Got parsed data from facebook') #parsed data can fail because of signing issues if 'oauth_token' in parsed_data: logger.info('Got access_token from parsed data') # we already have an active access token in the data access_token = parsed_data['oauth_token'] else: logger.info('Got code from parsed data') # no access token, need to use this code to get one code = parsed_data.get('code', None) if not access_token: if code: cache_key = 'convert_code_%s' % code access_token = cache.get(cache_key) if not access_token: # exchange the code for an access token # based on the php api # https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php # create a default for the redirect_uri # when using the javascript sdk the default # should be '' an empty string # for other pages it should be the url if not redirect_uri: redirect_uri = '' # we need to drop signed_request, code and state redirect_uri = cleanup_oauth_url(redirect_uri) try: logger.info( 'trying to convert the code with redirect uri: %s', redirect_uri) #This is realy slow, that's why it's cached token_response = FacebookAuthorization.convert_code( code, redirect_uri=redirect_uri) expires = token_response.get('expires') access_token = token_response['access_token'] #would use cookies instead, but django's cookie setting #is a bit of a mess cache.set(cache_key, access_token, 60 * 60 * 2) except open_facebook_exceptions.OAuthException, e: # this sometimes fails, but it shouldnt raise because # it happens when users remove your # permissions and then try to reauthenticate logger.warn('Error when trying to convert code %s', unicode(e)) if raise_: raise else: return None elif request.user.is_authenticated(): #support for offline access tokens stored in the users profile profile = request.user.get_profile() access_token = getattr(profile, 'access_token', None) if not access_token: if raise_: message = 'Couldnt find an access token in the request or the users profile' raise open_facebook_exceptions.OAuthException(message) else: return None else: if raise_: message = 'Couldnt find an access token in the request or cookies' raise open_facebook_exceptions.OAuthException(message) else: return None
def get_facebook_graph(request=None, access_token=None, redirect_uri=None): ''' given a request from one of these - js authentication flow (signed cookie) - facebook app authentication flow (signed cookie) - facebook oauth redirect (code param in url) - mobile authentication flow (direct access_token) - offline access token stored in user profile returns a graph object redirect path is the path from which you requested the token for some reason facebook needs exactly this uri when converting the code to a token falls back to the current page without code in the request params specify redirect_uri if you are not posting and recieving the code on the same page ''' #should drop query params be included in the open facebook api, maybe, weird this... from open_facebook import OpenFacebook, FacebookAuthorization parsed_data = None expires = None if hasattr(request, 'facebook'): graph = request.facebook _add_current_user_id(graph, request.user) return graph if not access_token: #easy case, code is in the get code = request.REQUEST.get('code') if not code: #signed request or cookie leading, base 64 decoding needed signed_data = request.REQUEST.get('signed_request') cookie_name = 'fbsr_%s' % facebook_settings.FACEBOOK_APP_ID cookie_data = request.COOKIES.get(cookie_name) if cookie_data: signed_data = cookie_data #the javascript api assumes a redirect uri of '' redirect_uri = '' if signed_data: parsed_data = FacebookAuthorization.parse_signed_data(signed_data) if 'oauth_token' in parsed_data: # we already have an active access token in the data access_token = parsed_data['oauth_token'] else: # no access token, need to use this code to get one code = parsed_data.get('code', None) if not access_token: if code: #exchange the code for an access token #based on the php api #https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php #create a default for the redirect_uri #when using the javascript sdk the default should be '' an empty string if not redirect_uri: redirect_uri = '' #we need to drop signed_request, code and state redirect_uri = cleanup_oauth_url(redirect_uri) try: logger.info('trying to convert the code with redirect uri: %s', redirect_uri) token_response = FacebookAuthorization.convert_code(code, redirect_uri=redirect_uri) expires = token_response.get('expires') access_token = token_response['access_token'] except open_facebook_exceptions.OAuthException, e: #this sometimes fails, but it shouldnt raise because it happens when users remove your #permissions and then try to reauthenticate logger.warn('Error when trying to convert code %s', unicode(e)) return None elif request.user.is_authenticated(): #support for offline access tokens stored in the users profile profile = request.user.get_profile() access_token = getattr(profile, 'access_token', None) if not access_token: return None else: return None