Can a facebook user opt out of the graph api? - facebook-graph-api

I am using spring social to get a list of all the my friends. However, it seems that the number of friends I can get from the facebook graph api is not the same as the number of friends that facebook reports on my profile.
According to facebook I have 175 friends. Using spring social facebook I can only seem to get 156 friends. I know about the paging issues with spring social facebook so I am using the code below.
private Map<String, FacebookProfile> getFacebookProfiles()
{
Connection<Facebook> connection = connectionRepository.findPrimaryConnection(Facebook.class);
FriendOperations friendOperations = connection.getApi().friendOperations();
// FindFriendProfes() only returns 100 friends, need to use batching to get all friends
List<FacebookProfile> friendProfiles = new ArrayList<>(250);
final int batchSize = 100;
int offset = 0;
List<FacebookProfile> batch = friendOperations.getFriendProfiles(offset, batchSize);
do
{
friendProfiles.addAll(batch);
offset = offset + batchSize;
batch = friendOperations.getFriendProfiles(offset, batchSize);
} while (batch.size() != 0);
Map<String, FacebookProfile> result = new HashMap<>(friendProfiles.size());
for (FacebookProfile facebookProfile : friendProfiles)
{
result.put(facebookProfile.getId(), facebookProfile);
}
return result;
}
Either there is a bug in spring-social facebook integration or there is a way for facebook users to say that they should not be returned as part of graph api call.
Can a facebook user opt of being returned as a friend from the graph api?

Yes precisely, Facebook users can opt out of third party applications by going to their Privacy Settings > Ads, Apps and Websites > Edit Settings > How people bring your info to apps they use
Those friends who edited those settings will not show up in your graph API calls.

Related

Store UserTokens generated by ASP.Net Core identity for external login provider

I am using Facebook as external login of ASP.Net Core identity.
I would like, even if the user logged in with Facebook, the user to fill his profile on the website.
For that I use the ExternalLoginCallback method, from which I would like to get data from Facebook such as date of birth, location (country), ...
One issue is if the user unchecked some of the permissions, the default call to Facebook fails:
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
return RedirectToAction(nameof(Login));
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
I would also need to do some additional checks on user data, which would require calling directly the Graph API.
My blocking points:
- In the ExternalLoginCallback method, I would need to separate the 'country' and 'birthday' to avoid the Facebook API to return an error in case of the user didn't grant the permission
- For that I would need the the user access_token (and for additional calls in the method), I don't see how to get it even if it is used by the Facebook Identity framework
- Once the profile created, I would like to get access to this access_token, which should be stored in the UserTokens table (I guess?), but I can't find it there, the table is empty. (my DbContext is a class extending IdentityDbContext<AppUser, AppRole, long>, don't know if it has an impact)
I have found this answer https://stackoverflow.com/a/42670559/4881677 which may help, but not sufficient.
Any help? :)
In order to store the user Facebook token, it requires to specify it in the options (not stored by default).
var fo = new FacebookOptions();
fo.SaveTokens = true;
From there we can call the graph method permissions to get the available permissions: https://graph.facebook.com/me/permissions?access_token={token}
Then it can be read with something like this:
foreach (var perm in data)
{
perms.Add((string)perm["permission"], (string)perm["status"]);
}

Specifying a FB Page for a desktop/mobile news feed ad using Python SDK

I'm new to the Facebook Ads Python SDK and am trying to create an ad that will display on mobile and desktop news feeds. Reviewing the web flow to create an ad, to show up in a feed, the ad needs to be related to a Facebook page. I manage a Facebook page, but am not sure where I need to input the FB page info.
I've reviewed the documentation and getting started examples, however the getting started is about a page post ad, so not relevant to me. I assumed "Placements" would be relevant but nothing I find there is helpful either. I also looked at Connection objects but that also seemed a dead end.
The code I've got runs and creates ads that are right column ads. I've tried adding "page_types: ['feed']" to AdSet targeting but that returns an error saying that placement is ineligible for this ad.
Could someone point me in the right direction on how to create an ad that will show up in the desktop news feed? Meat of the code is below.
`
# create campaign
campaign = AdCampaign(parent_id=config['act_account_id'])
campaign[AdCampaign.Field.name] = 'Test Campaign'
campaign[AdCampaign.Field.status] = AdCampaign.Status.paused
campaign[AdCampaign.Field.objective] = AdCampaign.Objective.website_clicks
campaign.remote_create()
# create adset
targeting = {
#'page_types': ['feed'],
'geo_locations': {
'cities': [{'key': '2532348', 'radius': 25, 'distance_unit': 'mile'}]
}
}
data = {
AdSet.Field.name: 'My Python SDK AdSet',
AdSet.Field.bid_type: AdSet.BidType.cpc,
AdSet.Field.is_autobid: True,
AdSet.Field.status: AdSet.Status.active,
AdSet.Field.lifetime_budget: 500,
AdSet.Field.end_time: '2015-06-4 23:59:59 PDT',
AdSet.Field.campaign_group_id: config['campaign_id'],
AdSet.Field.targeting: targeting,
}
adset = AdSet(parent_id=config['act_account_id'])
adset.remote_create(params=data)
# Set up creative for ad
image = AdImage(parent_id=config['act_account_id'])
image[AdImage.Field.filename] = image_filename
image.remote_create()
creative = AdCreative(parent_id=config['act_account_id'])
creative[AdCreative.Field.name] = 'sample creative'
creative[AdCreative.Field.title] = 'ad headline'
creative[AdCreative.Field.body] = 'ad text'
creative[AdCreative.Field.object_url] = 'www.http://www.misc-url.com'
creative[AdCreative.Field.image_hash] = image[AdImage.Field.hash]
creative.remote_create()
# Create your ad by referencing the ad creative.
adgroup = AdGroup(parent_id=config['act_account_id'])
adgroup[AdGroup.Field.name] = 'Testing SDK Ad'
adgroup[AdGroup.Field.campaign_id] = config['adset_id']
adgroup[AdGroup.Field.creative] = {'creative_id': str(creative_id)}
adgroup[AdGroup.Field.status] = AdGroup.Status.paused
adgroup.remote_create()
`
There is an api call to create a page post at the time of ad creation:
https://developers.facebook.com/docs/marketing-api/adcreative/v2.3#object_story_spec
See the blog post for more detailed info:
https://developers.facebook.com/ads/blog/post/2014/08/28/creative-page-post-api

Retrieving Google User Photo

I am able to retrieve the thumbnailPhotoUrl from the user.list api of the google admin SDK. However, whenever I try to render the image, Google is redirecting to a static silhouette image. The URL that is retrieved via the API looks like
https://plus.google.com/_/focus/photos/private/AIbEiAIA....
As mentioned, this ends up getting redirected to:
https://ssl.gstatic.com/s2/profiles/images/silhouette200.png
However, with a little bit of reverse engineering, I can see the photo by adding /u/1/ to the beginning of the URL path, like this:
https://plus.google.com/u/1/_/focus/photos/private/AIbEiAIA...
From my research the /u/1 has something to do with multiple google accounts, so I'm afraid I wouldn't be able to rely on this method. Can anyone help me understand what's happening here?
Out of my own experience I figured out that if the thumbnailPhotoUrl has private on the URL then the photo is not public i.e. not viewable outside the domain, it could also be that the user hasn't activated their Google+ profile, which I believe makes their photo public anyway.
Best to avoid using the thumbnailPhotoUrl if the URL has a private path on it. I think it's more reliable to retrieve the photo as Web-safe base64 data using the Users.Photo API then encode it as an inline base64 CSS image.
This is the code snippet I usually use:
import com.google.common.io.BaseEncoding;
import com.google.api.services.admin.directory.model.User;
import com.google.api.services.admin.directory.model.UserPhoto;
public class PhotoUtils {
public void loadPhoto() {
// if the user have not signed up for Google+ yet then their thumbnail is private
String thumbnailUrl = user.getThumbnailPhotoUrl();
String photoData = "";
if((thumbnailUrl != null) && (thumbnailUrl.indexOf("private") > -1)) {
UserPhoto photo = getUserPhoto(user.getId());
if(photo != null) {
photoData = getBase64CssImage(photo.getPhotoData(), photo.getMimeType());
}
}
}
public static String getBase64CssImage(String urlSafeBase64Data, String mimeType) {
urlSafeBase64Data = new String(BaseEncoding.base64().encode(
BaseEncoding.base64Url().decode(urlSafeBase64Data)));
return "data:" + mimeType + ";base64," + urlSafeBase64Data;
}
public UserPhoto getUserPhoto(String userId) throws IOException {
UserPhoto photo = null;
try {
photo = this.getClient().users().photos().get(userId).execute();
} catch(GoogleJsonResponseException e) {
if(e.getMessage().indexOf(NOT_FOUND) == 0) {
log.warning("No photo is found for user: " + userId);
} else {
throw e;
}
}
return photo;
}
}
Here are my 10 cents...
Recently I've been working on building a G Suite User Picker for Angular and I ran into the same problem. Nonetheless, I kept testing and researching and in one of my tests I decided to call the directory api users list endpoint with a NON-ADMIN user. (Head Explodes 🤯) To my surprise, google permits this type of requests, although it provides non super admin data but enough to get a private thumbnail url for the user. At first I thought it was an unintended behaviour, some kind of bug if you will. That is when I ran into this documentation.
While user accounts can only be modified by administrators, any user on the domain can read user profiles. A non-admin user can make a users.get or users.list request with the viewType parameter equal to domain_public to retrieve a user's public profile. The scope https://www.googleapis.com/auth/admin.directory.user.readonly is ideal for this use case.
So it is an intended behaviour. That is when it hit me. The private url generated when retrieving the user data is intended to be used only by the requesting user. If you provide the url to other user, it will redirect to the anonymous silhouette.
From this I was able to conclude that if I need to retrieve a list of users and need the ability to view their photos, I need to call the directory api endpoint using the credentials of the user that needs to view the respective data. This serves my purpose with the G Suite User Picker widget I am building.

Accessing signed Facebook JSON using Play 2.0.1 and RestFB

I'm writing an Play 2.0.1 application that will be deployed within a canvas page on Facebook. I'm using restFB for the Facebook API.
By manually creating an Access Token at https://developers.facebook.com/tools/explorer?method=GET I can get my application to access the Facebook user's name and render it in the Canvas page iFrame on Facebook.
So, my app controller receives the POST and renders a page like this:
public static Result handle_fb_post() {
return redirect(routes.canvas.fb_render_profile_page());
}
public static Result fb_render_profile_page() {
String accessToken = "<access token copied from facebook graph explorer>";
DefaultFacebookClient().getExtendedAccessToken(application_id, application_secret);
FacebookClient facebookClient = new DefaultFacebookClient(accessToken);
User fbUser = facebookClient.fetchObject("me", com.restfb.types.User.class);
return ok(views.html.fbuser.render(fbUser));
}
My routes are set up as:
POST /fbcanvas/ controllers.FBCanvas.handle_fb_post()
GET /fbcanvas/profile controllers.FBCanvas.fb_render_profile_page()
However, how can I get access to the signed JSON that Facebook sends me in the POST? I believe that this also contains the access token from the user (assuming that they have authorised my app) as described here:
https://developers.facebook.com/docs/samples/canvas/ (See section on "signed_request Parameter").
There are a number of examples out there for Java, PHP, Javascript and Python, but I can't find any that describe how to do this in Play 2 or using RestFB. I have tried a number of different approaches, but cannot work out how to access the signed request in Play 2.
Can anyone help and explain how this can be done?
If the token is sent as Json in the POST request, you can grab it via
def handle_post() = Action { implicit request =>
val jsonBody = request.body.asJson
//process it
}

facebook create_event posts on my app's wall not the user's wall

i'm attempting to provide a facility on my site that allows a user to create a facebook event for their booking.
http://developers.facebook.com/docs/reference/api/event/
now im doing the correct process:
1) first getting authorisation from the user
https://graph.facebook.com/oauth/authorize?client_id=APP_ID&redirect_uri=http://urlimredirectingto.comtype=web_server
2) requesting for an access token with the "code" that is returned in step 1
https://graph.facebook.com/oauth/access_token
3) using the access_token to create the event ...
string facebookCreateUri = string.Format("https://graph.facebook.com/{0}/events", loggedInMember.FacebookUID);
var formData = new HttpUrlEncodedForm()
{
{"access_token", accessToken},
{"owner", loggedInMember.FacebookUID},
{"description", "nice event that should be on the owners wall"},
{"name", "event on the users wall"},
{"start_time", "1272718027"},
{"end_time", "1272718027"},
{"location", "rochester"},
{"privacy","OPEN"}
};
HttpContent content = HttpContent.Create(formData);
HttpClient client = new HttpClient();
var response = client.Post(facebookCreateUri, "application/x-www-form-urlencoded", content);
but the event is posted on my app's wall, not the user's wall. It shouldn't have anything to do with the authentication/access_token elements because i use the same process to post on the user's wall. (http://developers.facebook.com/docs/reference/api/status/) and that works just fine.
I came back with a solution, after a week of working at many features with Facebook SDK, it finally works!
protected void onPostEvent(object sender, EventArgs e)
{
if (CanvasAuthorizer.Authorize())
{
var fb = new FacebookWebClient(CanvasAuthorizer.FacebookWebRequest);
dynamic parameters = new ExpandoObject();
parameters.description = txtEvDett.Text;
parameters.name = txtEvName.Text;
parameters.start_time = DateTime.Now.ToString("yyyyMMdd");
parameters.end_time = DateTime.Now.AddDays(1).ToString("yyyyMMdd");
parameters.access_token = CanvasAuthorizer.FacebookWebRequest.AccessToken;
dynamic eventDet = fb.Post("me/events", parameters);
litEvent.Text = String.Format("You have created the event with ID: {0}", eventDet.id);
lnkEvent.Visible = true;
lnkEvent.NavigateUrl = String.Format("http://www.facebook.com/event.php?eid={0}", eventDet.id);
}
}
For events, you have to request the create_event permission.
You should use /me/events to post on your events.
I user the C# SDK for Facebook from Codeplex - last version available for dld (aug 2011 - v5.2.1).
Good luck!
I don;t see in your request for Authorization any permission.. base permissions are not enough to do the postings.
i used:
https://www.facebook.com/dialog/permissions.request?app_id=MY_APP_ID&next=MY_APP_URL&display=page&response_type=code&canvas=1&perms=publish_stream,user_about_me,email
This is in the context of a canvas app. where MY_APP_URL is the url from facebook of the app:
http://apps.facebook.com/MY_APP_NAME_OR_ID
See extended permissions for events and check event's page in documentation
[EDIT] - I came back, sorry, now i did a test, and indeed, it works for me, but only of i post on my app's wall; even if i provided the 'user_events' permission i get this error:
The remote server returned an error: (403) Forbidden when posting on a user's wall.
This being said, i also subscribe to this question.