Facebook - Are granted permissions cumulative? - facebook-graph-api

Context
From Facebook Best Pratices I've understand that I should request a minimum set of permissions on the initial login page and delay the request of extended permissions for when they are really required.
For example. Say that my original login request for two extended profile properties:
<fb:login-button show-faces="true" width="200" max-rows="1"
scope="user_photos, friends_photos">
</fb:login-button>
Now, at some latter point, the user wishes to upload a photo back their profile using my app (which requires publish_actions).
As I understand it my App have to:
Check if the user have already granted that permission (say, with FB.api('/me/permissions')) to avoid triggering a login flow for no reason
Ask for the new permission by means of performing a new login:
FB.login(function(response) {
// handle the response
}, {scope: 'publish_actions'});
Perform a second permission check to see if the user have granted the new permission. If the user have granted the new permission perform the upload, else display some kind of error message explaining to the user that he should grant the new permission before being able to upload.
And my doubts are:
On the second step outlined above, should I ask only for publish_actions or should I also request already granted permissions?
{scope: 'user_photos, friends_photos, publish_actions'});
I'm using a Login on Client, API Calls from Server model:
So, at the moment that I request the new permission, my server will be holding a long-lived access token with the two initial permissions (user_photos, friends_photos). If the user grants publish_actions, am I supposed to go through the entire server side token exchange process again (using the new short lived access-token) before uploading?
GET /oauth/access_token?
grant_type=fb_exchange_token&
client_id={app-id}&
client_secret={app-secret}&
fb_exchange_token={new-short-lived-token}
Or will the new permission be promptly available for the long-lived token?

Answering my own question to anyone facing the same problem.
Yes, granted permissions are cumulative.
Should I ask only for publish_actions or should I also request already granted permissions?
I only need to ask for new permissions. Previously granted permissions remain available.
If the user grants publish_actions (with FB.login), am I supposed to go through the entire server side token exchange process again (using the new short lived access-token) before uploading? Or will the new permission be promptly available for the long-lived token?
The client side call to FB.login suffices. If the user grants publish_actions I can use the previously stored server side token to post content.
One small gotcha: When you call FB.login the user may skip the permission again (and it will return response.status === "connected"). So I had to actually double check for permissions:
Function to check permissions with callbacks:
function checkPermissions(perms, callback, failCallback) {
FB.api('/me/permissions', function (response) {
var fbPerms = response.data[0];
var haveAllPermissions = true;
if (typeof perms === 'string') {
perms = [ perms ];
}
for (var i in perms) {
if (fbPerms[perms[i]] == null) {
haveAllPermissions = false;
break;
}
}
if (haveAllPermissions) {
callback();
} else {
failCallback();
}
});
}
Assuming that photoUpload is a function that requires the publish_actions permission, here is the usage pattern that worked for me:
// First check
checkPermissions("publish_actions",
// If the required permissions have already been granted
// call the desired method
photoUpload,
// else
function () {
// Asks for permission
FB.login(function () {
// double check - the usar may have skiped the permission again
checkPermissions("publish_actions",
// if the user granted the permission, call the desired method
photoUpload,
// else cancel everything and warn the user
function () {
alert("Can't post without permissions");
});
}, {scope: "publish_actions"});
});

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"]);
}

Meteor share sessions data between client and server

I'm building a restricted signup. I want user with a specific code passed in a url to be able to signup and not others. I'm using the accounts package.
I can prevent account creation in the Accounts.onCreateUser method. I'm looking for a way to tell the server if the client had an authorised signup code. With a classic form (email+password) I can just add an extra hidden field. How can I achieve the same result if the user signs up with let's say Facebook?
Since Meteor doesn't use cookies, I can't store this info in a cookie that the server would access. Session variable are not accessible server side. And since I'm not controlling what got send with the account-facebook creation, I can't use a Session variable on the client side that I'd pass along when the user presses sign up.
Any idea"?
Just add the special token to the user object being passed to Accounts.createUser():
var user = {
email: email,
password: password,
profile: {
token: token
}
};
Accounts.createUser(user, function (error, result) {
if (error) {
console.log(error)
}
});
On the server side you can access this in the Accounts.onCreateUser():
Accounts.onCreateUser(function(options, user) {
console.log(options);
console.log(user);
});
I think it's in the options variable that you will find your token, so it would be options.profile.token.
for me, the best option here was passing in custom parameters to loginbuttons.
see the package docs:
https://github.com/ianmartorell/meteor-accounts-ui-bootstrap-3
Where it outlines the below:
accountsUIBootstrap3.setCustomSignupOptions = function() {
return {
mxpDistinctId: Session.get('mxpdid'),
leadSource: Session.get('leadSource')
}
};

Facebook PHP SDK 4.0 - Cannot re-ask read permissions once denied

I'm currently asking users for two read permissions i.e. email and user_location and one write permssion i.e. publish_actions. Following is the code snippet I'm using to verify if the user has granted all requested permissions:
$facebook = new Facebook(APP_ID, APP_SECRET, REDIRECT_URI);
if ( $facebook->IsAuthenticated() ) {
// Verify if all of the scopes have been granted
if ( !$facebook->verifyScopes( unserialize(SCOPES) ) ) {
header( "Location: " . $facebook->getLoginURL( $facebook->denied_scopes) );
exit;
}
...
}
Facebook is a class I've customly built to wrap the login flow used by various classes in the SDK. IsAuthenticated() makes the use of code get variable to check if the user is authorized. verifyScopes() checks granted permissions against SCOPES and assings an array of denied scopes to denied_scopes property. getLoginURL()` builds a login-dialog URL based on permissions passed as an an array as a only paramter.
Now, the problem is when the user doesn't grant write permissions, publish_actions in this case, write permission dialog is shown until user grants the write permission. But if the user chooses to deny of the read permissions, say email, the read login dialog isn't show. Instead Facebook redirects to the callback URL (that is REDIRECT_URI) creating a redirect loop.
The application I'm builiding requires email to be compulsorily provided but apparently the above approach (which seems to be the only) is failing. So, is there a workaround or a alternative way to achieve this? Or Facebook doesn't allow to ask for read permissions once denied?
As of July 15, 2014, an update has been made to the Facebook PHP SDK 4.x that allows user to re-ask the declined permissions. The function prototype of getLoginUrl() now looks like this.
public function getLoginUrl($redirectUrl, $scope = array(), $rerequest = false, $version = null)
So, to re-ask declined permissions we'd do something like this:
<?php
// ...
$helper = new FacebookRedirectLoginHelper();
if ($PermissionIsDeclined) {
header("Location: " . $helper->getLoginUrl( $redirect_uri, $scopes, true );
exit;
}
// ....
?>
For the time being you can append &auth_type=rerequest to the getLoginUrl return value to enable a rerequest, kind of lame but it works.
$helper = new FacebookRedirectLoginHelper($app_url, $app_id, $app_secret);
$login = $helper->getLoginUrl(array('scope'=>'user_likes', 'user_location'));
print $login."&auth_type=rerequest";
what about getReRequestUrl(); ?
That works just fine.
Read more at https://developers.facebook.com/docs/php/FacebookRedirectLoginHelper/5.0.0

the target user has not authorized this action facebook graph api

I am trying to post on user wall on behalf of the application.
Here's what I am doing.
First I get the user access_token from facebook connect and then I reciever an extended access token by using the following call
https://graph.facebook.com/oauth/access_token?
client_id=APP_ID&
client_secret=APP_SECRET&
grant_type=fb_exchange_token&
fb_exchange_token=EXISTING_ACCESS_TOKEN
Now when I try to post on friends wall with the following code.
var params = {};
params['message'] = 'Check out this new gag I\'ve posted about you surely it will piss you off :p';
params['name'] = 'Gagsterz';
params['description'] = 'If you also want to have fun on gagster join in now.';
params['link'] = 'http://localhost/php/backup/View/Profile.php';
params['picture'] = thumbpath;
params['caption'] = 'Caption';
FB.api('/'+victimid+'/feed?access_token="ACCESS_TOKEN", 'post', params, function(response) {
if (!response || response.error) {
var json = $.parseJSON(response.error);
alert(json.code);
alert(JSON.stringify(response.error));
} else {
alert('Published to stream - you might want to delete it now!');
}
});
It gives the following error.
the target user has not authorized this action facebook graph api
with code 200.
I take the permission publish_stream and I've tested it by reverting the permission and then logging in to give permissions to it again and the login windows asks for post on behalf permission.But I am taking the permission while I am retrieving the access token for the first time. Do I have to take the permission again for extended access token ? if yes then how to do so ?
Check your permissions, Do you have permission to Post on wall. By default you will have only read permission.
Check this http://developers.facebook.com/docs/reference/rest/stream.publish/

I got empty response from create album in facebook graph api?

i have this URL ..
https://graph.facebook.com/602414132/albums?access_token=123005381082600|2.FUPHmHF4kfDPly2GRPYDeg__.3600.1298228400-602414132|1JUKjvfo1Ri5s04x9v_vzf-sS8c&type=post&name=refacingme
when i using Chrome i get empty response :
{
"data": [
]
}
from where do you think the error is coming ??
It is true, I just fixed this bug a few days ago myself. The user must grant Extended Permissions for user_photos on your app. A few ways to do this:
1) Update your app info in FB Developer tab and add Extended permissions to your app. This will ask new users who register to give certain permissions before joining.
2) Alternatively, if the user has already added the app you can ask the user for permissions using the Javascript SDK call FB.login while they are in the app. (see code below)
FB.login(function(response) {
if (response.authResponse) {
// user is logged in and granted some permissions.
console.log("user is logged in and granted some permissions.")
} else {
// User cancelled login or did not fully authorize.
console.log("he failed to login or something")
}
}, {scope:'user_photos'});
This route requires you have set up the Facebook JavaScript SDK
I think you have not taken Extended Permission of user_photos. Take extended permission then you will not receive empty array