I am working on a python AppEngine application with Facebook login. I want to be able to access a logged-in user's friends' bios (their "about me" information).
Having requested/saved changes for all of the permissions I need on the Facebook developer's dashboard, I can successfully make calls like this:
friends = graph.get_connections("me", "friends", fields="name")
for friend in friends["data"]:
print friend["name"]
But trying to access the about me, as specified by the "bio" field in the docs, results in a key error:
friends = graph.get_connections("me", "friends", fields="name,bio")
for friend in friends["data"]:
print friend["bio"]
AppEngine log when running on local host:
File "/Users/nicole/aggie-harmony/main.py", line 154, in get
user = self.current_user
File "/Users/nicole/aggie-harmony/main.py", line 108, in current_user
bio = friend["bio"]
KeyError: 'bio'
INFO 2013-11-16 02:59:27,243 module.py:608] default: "GET / HTTP/1.1" 500 -
I have experimented with other fields, and it seems like anything that requires an access token will generate a key error (although the docs actually seem to have been updated mere HOURS before this question was posted...and they now suggest that "Other fields that have been explicitly set to public visibility can also be retrieved without an access token"...in my test user, the bio is indeed public!)
Unless I am just missing something syntax-wise, is this indeed a token issue? My app is based on the example here and basically obtains/uses the access token like this (auth stuff was generated previously and stored in its own file):
...
cookie = facebook.get_user_from_cookie(self.request.cookies, auth.FACEBOOK_APP_ID,
auth.FACEBOOK_APP_SECRET)
...
graph = facebook.GraphAPI(cookie["access_token"])
...
Interestingly enough, the app doesn't pop up a permission box for the user after the log-in area, which might support my hunch...
SOLVED: It was indeed an issue of permissions. In my adapted example html file, I just needed to change this:
function doLogin() {
FB.login(function(response) {} , {perms:''});
}
...to this:
function doLogin() {
FB.login(function(response) {} , {perms:'user_about_me,friends_about_me,user_likes,friends_likes,user_relationship_details,friends _relationship_details,user_relationships,friends_relationships, user_education_history, friends_education_history'});
}
Facebook takes care of the rest with the proper oauth dialog, etc.
Related
How to get FT articles via their API ?
After asking for a key I first used a python API wrapper around their content API, v2. So I ran the following:
from pyft import FT
ft = FT()
# the id can be pulled from the slug url of an FT.com story
content = ft.get_content("6f2ca3d6-86f5-11e4-982e-00144feabdc0")
print(content)
And got:
{'error': 'Access to this API has been disallowed'}
So I followed the official instructions but got the same error :
This should be a Bug from FT. Since, the get_content_notifications without any issues.
notification = ft.get_content_notifications("2018-10-10T00:00:00.000Z")
print(notification)
{
"requestUrl": "https://api.ft.com/content/notifications?since=2018-10-10T00%3A00%3A00.000Z",
"notifications": [
{
"type": "http://www.ft.com/thing/ThingChangeType/UPDATE",
"id": "http://www.ft.com/thing/e75d5a6c-b725-11e8-bbc3-ccd7de085ffe",
"apiUrl": "https://api.ft.com/content/e75d5a6c-b725-11e8-bbc3-ccd7de085ffe"
}
}
This is because your API key is under Headline License, which only allows access to limited enpoints. Therefore, you should consider purchasing a Datamining License in order to get access to more enpoints including the "get content" in your code. See more here: https://developer.ft.com/portal/docs-start-obtain-an-api-key
Instructions from FT.com
Download FT_API_Postman_Collection.json
Open Postman
Click Import, click Choose Files and specify FT_API_Postman_Collection.json. An import success message appears for each collection imported
Click the Eye icon to setup an Environment
Click Add
Enter an Environment name. for example, FTAPI
Copy your API Key from the email sent to you in the previous step
Enter a Key and a Value
Click Add
Click Add
When you setup an FTAPI environment, make sure the variable is named 'KEY' as this is what it is called in the Postman collection.json
seached and found that /me/accounts/ is no more available, in the past i used it, so i posted on a page's application without any problem, using that Gist,
But i deleted that application and tried to add another new one, when changed the code to:
class MainHandler(BaseHandler, tornado.auth.FacebookGraphMixin):
#tornado.web.authenticated
#tornado.web.asynchronous
def get(self):
self.facebook_request("/me/applications/developer/", self._on_accounts, access_token=self.current_user["access_token"])
def _on_accounts(self, account):
if account is None:
# Session may have expired
print "on accounts failed"
sys.exit()
print "here we go!"
print account # this will return me a json
for acc in account["data"]:
if acc["id"] == "157547381099830":
self.facebook_request("/157547381099830/feed", post_args={"message":"hi there!", "link": "http://www.example.com"}, access_token=self.current_user["access_token"], callback=self.async_callback(self._on_page_post))
what i can to do, is only post on my wall (changing self.facebook_request("/157547381099830/feed"... by self.facebook_request("/me/feed"...
)
I entered to the page, and Facebook knows that am the admin, but if i use the python code, it will publish the stream a normal user!
Edit: It seems that the problem is kind of new Facebook rules? when switch to my name, i cant publish on the wall, even if i'm the admin?
In the management setting, it is written:
Manager
Abdelouahab can manage admin roles, send messages and create posts as the Page, create ads, and view insights.
But, it seems it is not the case, because when i choose:
You are posting, commenting, and liking as Abdelouahab Abdenour Aliane — Change to Essog Community then i can post, only if i switch to Essog Community (because i blocked external users from posting on the wall).
Update: added manage_pages to the scope, and even this did not work
Sorry, found the answer:
Dident use manage_pages in the first time, so i used the page token!
i must get the page access token and not the user token!
To perform the following operations as a Page, and not the current user, you must use the Page's access token, not the user access token commonly used for reading Graph API objects.
(deleted and begin a new page)
this will work:
for acc in account["data"]:
if acc["id"] == "345632575539519":
print acc["access_token"]
self.facebook_request("/345632575539519/feed", post_args={"message":"hello", "link": "http://www.example.com"}, access_token=acc["access_token"], callback=self.async_callback(self._on_page_post))
this is my first web-programming experience so I hope my questions doesn't sound very dumb. I have been stucked on this for many days.
I am trying to understand a sample code:
https://github.com/facebook/runwithfriends
However I am not understanding very well how the information flow works and how can I modify that sample (i.e. how the code works).
For example, in the following section of the code:
class RecentRunsHandler(BaseHandler):
"""Show recent runs for the user and friends"""
def get(self):
if self.user:
friends = {}
for friend in select_random(
User.get_by_key_name(self.user.friends), 30):
friends[friend.user_id] = friend
self.render(u'runs',
friends=friends,
user_recent_runs=Run.find_by_user_ids(
[self.user.user_id], limit=5),
friends_runs=Run.find_by_user_ids(friends.keys()),
)
else:
self.render(u'welcome')
As I understand (along with HTML) is useful for showing friends that are using the same app, and if I understand correctly, here is the essential part:
*friends_runs=Run.find_by_user_ids(friends.keys())*
But what if I want to show any given friend. How can I do it?
Summarizing, I would like to know:
1- How the flow of the code works? (I don't fully understand the explanation here)
2- How can I manipulate the code so to get, for example, to show a list of friends of the user (not necessary that use the same app)?
Moreover, Can I show friends filtered by some characteristic (for example, gender)?
Thanks a lot!
The python "SDK" for facebook I use I took from https://gist.github.com/1190267
and combined it with the code from the example app to achieve the functionality I wanted both for a canvas app and for website usage.
It depends whether you're using facebook with websites or a canvas application. For a canvas application you probably could do well with the javascript SDK but for a "login with facebook" I required serverside logic that should work with javascript turned off so I've completed that solution with details you might have help to know. You can try make small changes of that specific app 'runwithfriends' to get an understanding which code does what. The project you're looking at contains some outdated practice though:
getting and setting cookies is likely preferable now doing with webapp2's builtin functions for this instead of the code that comes with the FB example app
logging in and out is now done with OAuth 2.0 so it's likely that the login system you're looking at is outdated and you need to use OAuth 2.0 which is described here. I much rather do login/logout serverside so I did an OAuth 2.0 pure python solution to login / logout following the authentication steps mentioned in the tutorial from FB. I had to clear the cookie to log a user out which was not documented.
To upgrade to python 2.7 I had to also modify so that HTTP header did not cast to unicode. I don't know why but otherwise it complained that headers were "not strings"
To more elaborately answer your specific questions:
1) The requesthandler class you posted is a subclass of a BaseHandler so to fully understand what it does you can look at the BaseHandler class since what you are posting is a BAseHandler. The BaseHandler uses django templates for rendering and if you want to can switch the template engine to jinja2 which is remmended. Further the code accesses the user object inherited from the BaseHandler and does some operations on it and renders it to a template. You can try make a requesthandler of your own, subclass BaseHandler and do what you want.
2) I could manipulate the code and I'm not an expert so you should be able to do it too. I wanted a simple FB app to display random images and I could manipulate it to select random images via blobs and render to to a template while keeping the facebook base functions. A function to use for getting the user using the Graph API I do this:
def parse_signed_request(signed_request, secret):
"""
Parse signed_request given by Facebook (usually via POST),
decrypt with app secret.
Arguments:
signed_request -- Facebook's signed request given through POST
secret -- Application's app_secret required to decrpyt signed_request
"""
if '.' in signed_request:
(esig, payload) = signed_request.split('.')
else:
return {}
sig = urlsafe_b64decode(str(esig))
data = _parse_json(urlsafe_b64decode(str(payload)))
if not isinstance(data, dict):
raise SignedRequestError('Pyload is not a json string!')
return {}
if data['algorithm'].upper() == 'HMAC-SHA256':
if hmac.new(secret, payload, hashlib.sha256).digest() == sig:
return data
else:
raise SignedRequestError('Not HMAC-SHA256 encrypted!')
return {}
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/.
"""
cookie = cookies.get('fbsr_' + app_id, '')
if not cookie:
return None
response = parse_signed_request(cookie, app_secret)
if not response:
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)
See http://developers.facebook.com/docs/api for complete documentation for the API. And you can get the the official Facebook JavaScript SDK at http://github.com/facebook/connect-js/
I'm now writing code to sync a webapp2_extras.auth account with facebook so that custom accounts and facebook accounts can co-exist and we're discussing solutions for this in the webapp2 groups and categories. The current way I do it is adding the recommended current_user to a basehandler and using that as the FB identity while working on "merging" my class FBUser that is a custom class for facebook users that autheorized my website and/or canvas application to sync with webapp2_extras.auth.models.User which is an expando model so it can just add the properties it doesn't have such as facebookid, firstname, lastname, etc.
#property
def current_user(self):
if not hasattr(self, '_current_user'):
self._current_user = None
cookie = get_user_from_cookie(self.request.cookies,
facebookconf.FACEBOOK_APP_ID,
facebookconf.FACEBOOK_APP_SECRET)
if cookie:
# Store a local instance of the user data so we don't need
# a round-trip to Facebook on every request
user = FBUser.get_by_key_name(cookie['uid'])
if not user:
graph = GraphAPI(cookie['access_token'])
profile = graph.get_object('me')
user = FBUser(key_name=str(profile['id']),
id=str(profile['id']),
name=profile['name'],
profile_url=profile['link'],
access_token=cookie['access_token'])
user.put()
elif user.access_token != cookie['access_token']:
user.access_token = cookie['access_token']
user.put()
self._current_user = user
return self._current_user
You can also solve your authentication with session objects and build your authentication system around that. That is what I do when using both custom accounts and facebook accounts and you're welcome to have a lok at my repository for more code examples how to intregrate facebook with google app engine using python 2.7.
I have an app engine web app that would like to automatically write a post to the wall of a facebook application I control (i.e. every time a particular event occurs on the website I would like to update the wall of my facebook application).
This code will be called from a deferred task on the server.
I have been unable to find anything addressing this. Your help would be appreciated.
First thing I did was get my access token with the following code:
https://graph.facebook.com/oauth/access_token?client_id=FACEBOOK_APP_ID&client_secret=FACEBOOK_APP_SECRET&grant_type=client_credentials&scope=manage_pages,offline_access
Using the returned access token this is what I'm running on the server:
form_fields = {
"access_token": FACEBOOK_ACCESS_TOKEN,
"message": tgText
};
form_data = urllib.urlencode(form_fields)
result = urlfetch.fetch(url="https://graph.facebook.com/MYAPP_FACEBOOK_ID/feed",
payload=form_data,
method=urlfetch.POST,
validate_certificate=False,
headers={'Content-Type': 'application/x-www-form-urlencoded'})
But calling this results in:
{"error":{"type":"OAuthException","message":"(#200) The user hasn't authorized the application to perform this action"}}
As an administrator you can grant access to third party apps (e.g. your python app) to post onto your App's Profile Page (http://www.facebook.com/apps/application.php?id=YOUR_APP_ID) using OAuth:
http://developers.facebook.com/docs/authentication/ (Section Page Login)
Once you received an access token you should be able to post to App Profile Page as described here:
http://developers.facebook.com/docs/reference/api/post/ (Section Publishing)
I have a similar app. Facebook can change the code that you're meant to submit as part of the process for gaining an access token. I wrote a simple web page that creates a form with hidden input fields that contain the data required to get Facebook to authorise an app with a user (see http://developers.facebook.com/docs/reference/dialogs/oauth/).
When the user clicks the submit button, the browser does an HTTP GET to Facebook, with an appropriate query string, where the Facebook user is prompted to login (if needed) and authorise the app. If authorised Facebook calls back your redirect_url with the code which you can store in the DataStore to retrieve when needed as part of the "give me an access token" flow.
I am trying to complete a story assignment system for my school newspaper in Google App Engine. It'll track deadlines for writers, allow writers to pick up stories, and give an "at a glance" view of the weeks stories. My partner and I are trying to fully integrate it with our newspapers Google Apps installation. Oh, and we have to use 3 legged Oauth because we don't have Google Apps Premier.
In that endeavor, I stumbled upon Aeoid and was able to follow the instructions to make federated login work. It's very cool!
Where I'm running into trouble is using Oauth to get a list of the users google documents. I have a test page set up here: mustrun.cornellsun.com/test. It is giving me errors - I've copied them at the bottom of this mail. I don't know if this has to do with my consumer secret (should I be using the key I get from google marketplace? or should I be using the key I get from the manage domains page?). Right now I'm using the key I got from the manage domains page
Also complicating this is that the actual appspot domain is mustrun2sun [].appspot[too new can't post more than one link].com, but I set it up in google apps so that only users from my domain can log in and also so that the app is deployed on my domain. (app is deployed as must[]run[].corn[]ellsun[].[]com & everything refers to it as such, even in the manage domains thing.)
I'm using GDClient 2.0 classes so I'm fairly sure that everything should work as planned... i.e. I'm not using the old service stuff or anything. I've used htt[]p:/[]/k[]ing[]yo-bachi.blog[]spot.c[]om/2010/05/gaego[]ogleoauth.ht[]ml as a bit of a template for my Oauth "dance" because the Google examples are out of date & use the old Google data 1.0 library - I think.
The error that I'm getting when I go to my test page is
Traceback (most recent call last):
File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/webapp/__init__.py", line 511, in __call__
handler.get(*groups)
File "/base/data/home/apps/mustrun2sun/1.341947133742569880/main.py", line 170, in get
feed = client.GetDocList(auth_token=gdata.gauth.AeLoad(users.get_current_user().user_id())) #auth_token=TOKEN
File "/base/data/home/apps/mustrun2sun/1.341947133742569880/gdata/docs/client.py", line 141, in get_doclist
auth_token=auth_token, **kwargs)
File "/base/data/home/apps/mustrun2sun/1.341947133742569880/gdata/client.py", line 635, in get_feed
**kwargs)
File "/base/data/home/apps/mustrun2sun/1.341947133742569880/gdata/client.py", line 308, in request
response, Unauthorized)
Unauthorized: Unauthorized - Server responded with: 401, <HTML>
<HEAD>
<TITLE>Token invalid - Invalid AuthSub token.</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Token invalid - Invalid AuthSub token.</H1>
<H2>Error 401</H2>
</BODY>
</HTML>
Also, since this is hard w/o any source code, below is the relevant code:
import gdata.auth
import gdata.gauth
import gdata.docs.client
import gdata.docs.data
import gdata.docs.service
import gdata.alt.appengine
from aeoid import middleware, users
class GetOauthToken(webapp.RequestHandler):
def get(self):
user_id = users.get_current_user().user_id()
saved_request_token = gdata.gauth.AeLoad("tmp_"+user_id)
gdata.gauth.AeDelete ("tmp_" + user_id)
request_token = gdata.gauth.AuthorizeRequestToken(saved_request_token, self.request.uri)
#upgrade the token
access_token = client.GetAccessToken(request_token)
#save the upgraded token
gdata.gauth.AeSave(access_token, user_id)
self.redirect('/test')
class Test(webapp.RequestHandler):
def get(self):
TOKEN = gdata.gauth.AeLoad(users.get_current_user().user_id())
if TOKEN:
client = gdata.docs.client.DocsClient(source=SETTINGS['APP_NAME'])
client.auth_token = gdata.gauth.AeLoad(users.get_current_user().user_id()) #could try to put back as TOKEN?
self.response.out.write('moo baby')
client.ssl = True
feed = client.GetDocList(auth_token=gdata.gauth.AeLoad(users.get_current_user().user_id())) #auth_token=TOKEN
self.response.out.write(feed)
self.response.out.write('moo boobob')
self.response.headers['Content-Type'] = 'text/plain'
for entry in feed.entry:
self.response.out.writeln(entry.title.text)
else:
# Get unauthorized request token
gdata.gauth.AeDelete(users.get_current_user().user_id())
client = gdata.docs.client.DocsClient(source=SETTINGS['APP_NAME'])
client.ssl = True # Force communication through HTTPS
oauth_callback_url = ('http://%s/get_oauth_token' %
self.request.host)
request_token = client.GetOAuthToken(
SETTINGS['SCOPES'], oauth_callback_url, SETTINGS['CONSUMER_KEY'],
consumer_secret=SETTINGS['CONSUMER_SECRET'])
gdata.gauth.AeSave(request_token, "tmp_"+users.get_current_user().user_id())
# Authorize request token
domain = None#'cornellsun.com'
self.redirect(str(request_token.generate_authorization_url(google_apps_domain=domain)))
I've been looking high and low on the web for an answer & I have not been able to find one.
I have a working python App Engine app that uses OpenID, and OAuth to get your google contacts:
http://github.com/sje397/Chess
It is running at:
http://your-move.appspot.com
Note that Aeoid is not needed anymore, since App Engine has built-in OpenID support.
I just found out wasting a couple of hours, that you get a 401 also if the URL is not correct.
In my example, I was doing
.../buzz/v1/activities/#me/#self**?&**alt=json
Instead of
.../buzz/v1/activities/#me/#self**?**alt=json
I have personally not worked with OAuth, but a few things I noticed that may (or may not) help:
The 401 error is likely an HTTP 401 error, which means that the url was valid but required authentication. This obviously is explained by the failed OAuth attempt, but it also might be important to redirect users who are not logged in to another page.
The error is occurring when you assign your feed variable. Is the auth_token parameter simply supposed to be a username?
3.You are using the line.
gdata.gauth.AeLoad(users.get_current_user().user_id())
frequently. Even though it might not be related to your auth problems, you would probably be better off making this query once and storing it in a variable. Then when you need it again, access it that way. It will improve the speed of your application.
Again, I apologize that I have had no specific OAuth experience. I just tried to scan and find some things that may spark you onto the right path.