Simple utilites for Facebook integration with your website.
Project description
facebook_utils
This library was created when a lot of things were possible - or worth doing - with the Facebook Graph API, and their official Python SDK did not offer some features and was no longer maintained.
Facebook's Graph API quickly lost many of it's worthwhile features. Eventually, they pushed for a new Business API and SDK that focuses on other things. Neither are particularly useful to accomplish the previously capable tasks. The utility of the Facebook service itself is increasingly questionable as well.
This library has been lightly updated to support legacy projects that once used the Graph API and Facebook Login. This library is not intended for robust usage or long term use.
The current project goals are to have proper typing and modern Python support for the remaining API utilities as projects move off this package and/or Facebook's API. New features are not intended, only increased code quality and integration.
Several tests are broken, as the functionality appears to longer work. Specifically, the unauthenticated tests for "test_graph__get_object_single" and "test_graph__get_object_multiple" no longer work, as they attempt to get the Facebook graph IDs for URLs - which is no longer supported via the Graph API.
Additionally, all tests will fail once API Rate Limits are exceeded.
A collection of utilities for integrating User Accounts with Facebook.com.
Right now this library is focused on oAuth Login and Graph API operations
This library has been a general-purpose Python client, operating in production for many years. The functionality may be limited, but it works well and ships with a detailed test suite.
Purpose
-
Once upon a time, Facebook offered a Python SDK. They dropped it. Boo.
-
When this was first released, there were no other Python SDKs actively developed.
-
There are a handful of Pyramid/Misc Framework utilities that provide a complete drop-in integration with Facebook.com for account logins and integrations; This is NOT one of them. Sometimes you need to control the User Experience and have all your UX customized; if so, this is for you.
Usage
All work is done via the facebook_utils.core.FacebookHub() object.
Configure a hub with something like the following:
from facebook_utils import FacebookHub
hub = FacebookHub(app_id = x,
app_secret = y,
enable_app_secretproof = True
)
Or make it unuthenticated. It's up to you.
This was originally built/intended for use under the Pyramid environment.
Calling FacebookPyramid() will create a new object that subclasses
FacebookHub() objects, using default settings from your .ini and
pulling variables from request as needed.
As of v0.5.0 it supports the appsecret_proof lockdown on the client level.
Any requests to the hub will attempt to create the appsecret_proof hmac if it
is not explicitly provided. It will be based on the access_token appearing as
(in the order of precedence):
- the
access_tokenkwarg to the `.api_proxy() method - an
access_tokenin the querystring of a retrieved url - an
access_tokenin the POST payload
This will allow you to follow paginated links from the API as-is, upgrading as needed.
IMPORTANT NOTES
Facebook's API Support is inconsistent with the terminology:
- The API endpoints expect
client_idandclient_secret - The Developer Tools provide
app idandapp secret
For the sake of clarity, this library uses the terms app_id and app_secret
because they are what Facebook's developer dashboard provides. They are translated
into the API Endpoint terms as required.
By default the API calls will be unversioned. You should specify the API version.
Supports Two oAuth Flows
Flow 1 - Server Side
- configure an object with
oauth_code_redirect_uri - consumers click a button on your site, which redirects to
oauth_code_redirect_uri-- as provided byoauth_code__url_dialog() - upon success, users are redirected from Facebook to
oauth_code_redirect_urialong with a query param titledcode - you may then call
.oauth_code__get_access_token()to get an access token or calloauth_code__get_access_token_and_profile()to get the token and profile data. - profile data can be updated with
.graph__get_profile(access_token)
Flow 2 - Client Side
- configure an object with
oauth_token_redirect_uri - consumers click a button on your site, which redirects to
oauth_token_redirect_uri-- as provided byoauth_token__url_dialog() - upon success, users are redirected from Facebook to
oauth_token__url_dialogalong with a query param titledtokenand a hash value titled#access_token. Theaccess_tokenis not visible to the server, and must be transferred to your server via JavaScript or not-at-all should you simply want to do all your integration in JavaScript. - profile data can be obtained with
.graph__get_profile(access_token)if you store the access token
Unauthenticated Queries
Queries without authentication required can be performed using the .api_proxy
method.
If you don't have any authentication data, you can create an unauthenticated hub, which allows you to leverage this library for streamlined requests and response processing.
hub = FacebookHub(unauthenticated_hub=True)
get_data = {'ids': "http://example.com", }
fb_data = hub.api_proxy(url="""https://graph.facebook.com""",
expected_format='json.load', get_data=get_data)
pprint.pprint(fb_data)
Rate Limiting?
Facebook may ratelimit requests.
The last response is stored FacebookHub._last_response for inspection.
A convenience method will check for the X-Page-Usage ratelimiting header:
print hub.last_response_ratelimited()
If no ratelimiting is set, it will return None.
If Facebook has set ratelimiting, it will convert the JSON-formatted string in the header into a python dict:
print hub.last_response_ratelimited()
> {"call_count" : x,
"total_time" : y,
"total_cputime" : z
}
Some Notes
Most methods will let you override the scope and request_uri.
This shouldn't really be necessary and will probably be deprecated.
Some methods support multiple ways of parsing results. Until recently, Facebook's API returned values either as url-encoded strings or as JSON. Now most results are in JSON.
Pyramid Examples
define some variables in your .ini files:
file: development.ini
# the default prefix is fbutils
fbutils.id = 123
fbutils.secret = 123
facebook.app.enable_secretproof = 1
fbutils.scope = email, user_birthday, user_checkins, offline_access
fbutils.oauth_code_redirect_uri = http://127.0.0.1:5010/facebook-oauth-redirect
fbutils.api_version = v2.8
fbutils.oauth_code_redirect_uri= http://127.0.0.1:5010/account/facebook-authenticate-oauth?response_type=code
fbutils.oauth_token_redirect_uri= http://127.0.0.1:5010/account/facebook-authenticate-oauth-token?response_type=token
or:
# customize the prefix!
fbutils.prefix = facebook.app
facebook.app.id = 123
facebook.app.secret = 123
facebook.app.enable_secretproof = True
facebook.app.scope = email, user_birthday, user_checkins, offline_access
facebook.app.oauth_code_redirect_uri = http://127.0.0.1:5010/facebook-oauth-redirect
facebook.app.api_version = v2.8
facebook.app.oauth_code_redirect_uri= http://127.0.0.1:5010/account/facebook-authenticate-oauth?response_type=code
facebook.app.oauth_token_redirect_uri= http://127.0.0.1:5010/account/facebook-authenticate-oauth-token?response_type=token
Make sure your endpoints are allowlisted on the Facebook console
Integrate into your views:
from facebook_utils import FacebookPyramid
class WebAccount(base.Handler):
def __new_fb_object(self):
"Create a new Facebook Object
note that we can override settings in the .ini files if we want
"
fb = FacebookPyramid(self.request,
oauth_code_redirect_uri = self.request.registry.settings['facebook.app.oauth_code_redirect_uri'],
oauth_token_redirect_uri = self.request.registry.settings['facebook.app.oauth_token_redirect_uri'],
)
return fb
def sign_up(self):
"sign up page, which contains a "signup with facebook link"
fb = self.__new_fb_object()
return {"project":"MyApp", 'facebook_pyramid':facebook}
@action(renderer="web/account/facebook_authenticate_oauth.html")
def facebook_authenticate_oauth(self):
fb = self.__new_fb_object()
(access_token,
profile
)= fb.oauth_code__get_access_token_and_profile(self.request)
if profile:
# congrats, they logged in
# register the user to your db
raise HTTPFound(location='/account/home')
else:
# boo, that didn't work
raise HTTPFound(location='/account/sign-up?error=facebook-oauth-failure')
return {"project":"MyApp"}
Integrate into your template:
<a class="fancy_button-1" id="signup-start_btn-facebook" href="${facebook_pyramid.oauth_code__url_dialog()}">
Connect with <strong>Facebook</strong>
</a>
Graph Operations
Every hub object has an api_proxy method, which can be used to
centralize communication to the Facebook API
Facebook's API isn't very 'standardized' across the board. Some endpoints
return json data, others return urlquoted data. api_proxy doesn't care.
it returns a dict from every endpoint, and does the conversion for you.
The api_proxy defaults to a json load. certain api calls will pass in
a different expected_format argument. The proxy will also handle "batch"
style graph requests.
When the api_proxy encounters an error, it returns ApiError or a more
contextual subclass of the that exception class.
The current exception class inheritance is:
ApiError
ApiAuthError
ApiAuthExpiredError
ApiApplicationError
ApiResponseError
ApiRuntimeError
ApiRuntimeVerirficationFormatError
ApiRuntimeGrantError
ApiRuntimeScopeError
ApiRuntimeGraphMethodError
ApiUnhandledError
AuthenticatedHubRequired
ApiError instances contain:
code (Facebook specific, not http code)
type (as dictacted by Facebook)
message (possibly dictated by Facebook)
raised (the trapped error that raised this, if available)
response (the repsonse in error, if available)
AuthenticatedHubRequired will be raised if a non-authenticated hub tries to
perform authenticated actions
The api_proxy will catch most errors. Since the Facebook API is in constant
flux, uncaught exceptions are raised. There will be a future "ApiUnhandledError".
Testing
Trial Run
-
Set up an App on https://developers.facebook.com
-
You need this information exported in your environment
FBUTILS_APP_ID: The numeric ID for the appFBUTILS_APP_SECRET: You generate this on the Facebook SettingsFBUTILS_ENABLE_SECRETPROOF: Enclose a HMAC secret signature? 1 or 0FBUTILS_APP_DOMAIN: the App's domain. example: dev.example.comFBUTILS_APP_SCOPE: The scope. can just be "email"FBUTILS_REDIRECT_URI_OAUTH_CODE: the uri to redirect visitors to. this MUST be whitelisted on Facebook's OAuth/Login settings
-
Run
python test-interactive.py
You will see a message like:
Visit the following url to approve.
You will be redirected back to the `FBUTILS_REDIRECT_URI_OAUTH_CODE` URI
> https://www.facebook.com/dialog/oauth?client_id={XXXXX}&scope={XXXXX}&redirect_uri={XXXXX}
Copy/paste that url into a browser window
You will be redirected to a url like:
> {{https://dev.cliqued.in/account/login/facebook-oauth?response_type=code}}&code={{CODE}}#_=_
Copy the entire CODE from the URL and paste it in. It is okay to leave the
trailing #_=_ fragment
This will step you through multiple API calls, asking you to visit the authorization URL each time.
You should notice the authorization code changes each time, however the Facebook API token which is returned will remain the same.
Unit Tests
Unit Tests require the following environment vars to be set:
FBUTILS_APP_ID
FBUTILS_APP_SECRET
FBUTILS_APP_SCOPE
FBUTILS_ACCESS_TOKEN
FBUTILS_APP_DOMAIN
FBUTILS_ENABLE_SECRETPROOF
it should be simple...
export FBUTILS_APP_ID="app_id_from_facebook.com"
export FBUTILS_APP_SECRET="app_secret_from_facebook.com"
export FBUTILS_APP_SCOPE="email,user_activities,user_status,user_posts"
export FBUTILS_APP_DOMAIN='allowlisted domain'
export FBUTILS_ACCESS_TOKEN="from_API_operations, or generate via developer interface"
export FBUTILS_ENABLE_SECRETPROOF=set if you locked this down on facebook
export FBUTILS_REDIRECT_URI_OAUTH_CODE= configured on the facebook dashboard
To generate a FBUTILS_ACCESS_TOKEN value, you can use the tests/generate_credentials.py
script.
Github Tests:
Managed by Github Secrets
FBUTILS_APP_SECRETFBUTILS_ACCESS_TOKEN
Set in .github/workflows/python-package.yml
FBUTILS_APP_IDFBUTILS_APP_SCOPEFBUTILS_APP_DOMAINFBUTILS_ENABLE_SECRETPROOFFBUTILS_REDIRECT_URI_OAUTH_CODEFBUTILS_REDIRECT_URI_OAUTH_TOKEN
Integrated Tests
There is also a test_interactive.py file that uses the same environment vars
python test_interactive.py
That will allow you to step through a few scenarios and set up an integration with Facebook itself.
:copyright: 2012-2021 by Jonathan Vanasco license: BSD
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file facebook_utils-0.60.0.tar.gz.
File metadata
- Download URL: facebook_utils-0.60.0.tar.gz
- Upload date:
- Size: 35.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fef2a28ebfe156ff720037c283a42f8b084fe311d5a6a4cb8491144b10da5119
|
|
| MD5 |
04b2482a3c83a7c8d99920327aef4d6f
|
|
| BLAKE2b-256 |
48cca2bc997d66989c930fb3a9199375a2c8570b0740bc2145bdc013217434fe
|