tornado
tornado copied to clipboard
Problem with nested requests when using the FacebookGraphMixin get_authenticated_user call
When using the FacebookGraphMixin and calling get_authenticated_user with the extra_fields parameter set to a list of fields that contain nested fields, the returned dictionary will not contain any fields which are nested.
With the Facebook graph api you can specify nested fields which contain modifier and sub selectors. For example you can specify limit by adding a .limit(nn) to a field name.
for example a call lilke:-
auth_info = yield self.get_authenticated_user( redirect_uri=my_url, client_id=client_id, client_secret=client_secret, code=self.get_argument('code'), extra_fields=['email', 'gender', 'age_range', 'permissions', 'friends.limit(5000)'])
will not return the friend list because it don't realise that .limit(5000) is a modifier and the returned field name is friends not friends.limit(5000).
the fix is relatively easy in auth.py by changing the method FacebookGraphMixin._on_get_user_info to remove modifiers prior to mapping the results. Something like this at the start of the method would do
fields = [field.split('.', 1)[0] for field in fields]
Per https://developers.facebook.com/docs/graph-api/guides/field-expansion, there are a few different formats this can take:
- "Limiters" such as
.limit(x)
,.width(x)
,.height(x)
. - Nested requests using curly-brace syntax such as
posts.limit(5){message}
(with or without a limiter) - Aliases, using the
as(new_name)
syntax.
field.split('.', 1)
only addresses the first. It would be simple to add the second if I could find a grammar for this field. The third form is much trickier and requires an exact match between tornado's grammar and facebooks to avoid smuggling attacks.
I'd be fine just not supporting this third case, but I'd rather not add support for the first case without the second, which means we still need some sort of grammar for this field.
Also, I think anyone affected by this has a workaround by not passing extra_fields to get_authenticated_user and instead making a second call with facebook_request where you can pass and parse whatever arguments you want.